Refactor a itsy bitsy tiny bit and add rook moves.
This commit is contained in:
parent
f79e5be008
commit
3d4fee2631
@ -183,97 +183,13 @@ func (b *Board) handleSpecialMove(move types.Move) bool {
|
||||
|
||||
switch pieceAtStartSquare.(type) {
|
||||
case Pawn:
|
||||
was = b.handlePossiblePromotion(move)
|
||||
pawn := pieceAtStartSquare.(Pawn)
|
||||
was = pawn.HandlePossiblePromotion(b, move)
|
||||
if !was {
|
||||
was = b.handleEnPassant(move, b.getLastMove())
|
||||
was = pawn.HandleEnPassant(b, move, b.getLastMove())
|
||||
}
|
||||
}
|
||||
return was
|
||||
}
|
||||
|
||||
func (b *Board) handlePossiblePromotion(move types.Move) bool {
|
||||
var isPromotionMove bool
|
||||
var promotionToPiece types.PieceShortName
|
||||
|
||||
//TODO(m): What if message does not contain a promotion, but should be because a pawn is moved to the end square
|
||||
messageContainsPromotion := move.IsPromotionMove()
|
||||
|
||||
if messageContainsPromotion {
|
||||
promotionToPiece = *move.PromotionToPiece
|
||||
}
|
||||
|
||||
switch move.ColorMoved {
|
||||
case types.White:
|
||||
if move.StartSquare.Row == types.RangeLastValid-1 &&
|
||||
move.EndSquare.Row == types.RangeLastValid {
|
||||
isPromotionMove = true
|
||||
}
|
||||
|
||||
case types.Black:
|
||||
if move.StartSquare.Row == types.RangeFirstValid+1 &&
|
||||
move.EndSquare.Row == types.RangeFirstValid {
|
||||
isPromotionMove = true
|
||||
}
|
||||
}
|
||||
|
||||
if isPromotionMove {
|
||||
delete(b.position, move.StartSquare)
|
||||
b.position[move.EndSquare] = GetPieceForShortName(promotionToPiece, move.ColorMoved)
|
||||
}
|
||||
|
||||
return isPromotionMove
|
||||
}
|
||||
|
||||
func (b *Board) handleEnPassant(move, lastMove types.Move) bool {
|
||||
var wasEnPassant bool
|
||||
|
||||
if lastMove.PieceMoved != types.PawnShortName {
|
||||
return false
|
||||
}
|
||||
|
||||
switch move.ColorMoved {
|
||||
case types.White:
|
||||
if lastMove.StartSquare.Row != 7 || lastMove.EndSquare.Row != 5 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.StartSquare.Row != 5 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.EndSquare.Row != 6 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
}
|
||||
case types.Black:
|
||||
if lastMove.StartSquare.Row != 2 || lastMove.EndSquare.Row != 4 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.StartSquare.Row != 4 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.EndSquare.Row != 3 {
|
||||
wasEnPassant = false
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
}
|
||||
}
|
||||
|
||||
if wasEnPassant { //play the move
|
||||
delete(b.position, lastMove.EndSquare)
|
||||
b.position[move.EndSquare] = GetPieceForShortName(move.PieceMoved, move.ColorMoved)
|
||||
}
|
||||
|
||||
return wasEnPassant
|
||||
}
|
||||
|
@ -117,10 +117,20 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) {
|
||||
assert.Equal(t, boardBeforeMove, board)
|
||||
})
|
||||
|
||||
// t.Run("king of moving color is in check after move", func(t *testing.T) {
|
||||
// good, _ := board.CheckMove(move)
|
||||
// assert.False(t, good)
|
||||
// })
|
||||
t.Run("king of moving color is in check after move", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
|
||||
board.position[types.Coordinate{Col: 1, Row: 6}] = King{Color: types.White}
|
||||
board.position[types.Coordinate{Col: 2, Row: 6}] = Pawn{Color: types.White}
|
||||
board.position[types.Coordinate{Col: 8, Row: 6}] = King{Color: types.Black}
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 2, Row: 6},
|
||||
EndSquare: types.Coordinate{Col: 2, Row: 7},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CheckMove_validPromotion(t *testing.T) {
|
||||
|
41
chess/free_squares.go
Normal file
41
chess/free_squares.go
Normal file
@ -0,0 +1,41 @@
|
||||
package chess
|
||||
|
||||
import "mchess_server/types"
|
||||
|
||||
func (b *Board) GetNonBlockedRowAndColumn(fromSquare types.Coordinate) []types.Coordinate {
|
||||
squaresLeft := fromSquare.GetAllSquaresLeft()
|
||||
squaresRight := fromSquare.GetAllSquaresRight()
|
||||
squaresAbove := fromSquare.GetAllSquaresAbove()
|
||||
squaresBelow := fromSquare.GetAllSquaresBelow()
|
||||
nonBlocked := []types.Coordinate{}
|
||||
|
||||
nonBlocked = append(nonBlocked, b.getNonBlocked(squaresLeft, fromSquare)...)
|
||||
nonBlocked = append(nonBlocked, b.getNonBlocked(squaresRight, fromSquare)...)
|
||||
nonBlocked = append(nonBlocked, b.getNonBlocked(squaresAbove, fromSquare)...)
|
||||
nonBlocked = append(nonBlocked, b.getNonBlocked(squaresBelow, fromSquare)...)
|
||||
|
||||
return nonBlocked
|
||||
}
|
||||
|
||||
func (b *Board) getNonBlocked(
|
||||
squaresToCheck []types.Coordinate,
|
||||
sourceSquare types.Coordinate,
|
||||
) []types.Coordinate {
|
||||
pieceOnSourceSquare := b.getPieceAt(sourceSquare)
|
||||
nonBlocked := []types.Coordinate{}
|
||||
|
||||
for _, square := range squaresToCheck {
|
||||
piece := b.getPieceAt(square)
|
||||
if piece != nil {
|
||||
if piece.GetColor() == pieceOnSourceSquare.GetColor() {
|
||||
break
|
||||
}
|
||||
// if there is an opposite colored piece we append it but
|
||||
// stop appending the squares behind it
|
||||
nonBlocked = append(nonBlocked, square)
|
||||
break
|
||||
}
|
||||
nonBlocked = append(nonBlocked, square)
|
||||
}
|
||||
return nonBlocked
|
||||
}
|
@ -21,6 +21,102 @@ func (p Pawn) GetColor() types.ChessColor {
|
||||
return p.Color
|
||||
}
|
||||
|
||||
func (p *Pawn) HandlePossiblePromotion(b *Board ,move types.Move) bool {
|
||||
var isPromotionMove bool
|
||||
var promotionToPiece types.PieceShortName
|
||||
|
||||
//TODO(m): What if message does not contain a promotion, but should be because a pawn is moved to the end square
|
||||
messageContainsPromotion := move.IsPromotionMove()
|
||||
|
||||
if messageContainsPromotion {
|
||||
promotionToPiece = *move.PromotionToPiece
|
||||
}
|
||||
|
||||
switch move.ColorMoved {
|
||||
case types.White:
|
||||
if move.StartSquare.Row == types.RangeLastValid-1 &&
|
||||
move.EndSquare.Row == types.RangeLastValid {
|
||||
isPromotionMove = true
|
||||
}
|
||||
|
||||
case types.Black:
|
||||
if move.StartSquare.Row == types.RangeFirstValid+1 &&
|
||||
move.EndSquare.Row == types.RangeFirstValid {
|
||||
isPromotionMove = true
|
||||
}
|
||||
}
|
||||
|
||||
if isPromotionMove {
|
||||
delete(b.position, move.StartSquare)
|
||||
b.position[move.EndSquare] = GetPieceForShortName(promotionToPiece, move.ColorMoved)
|
||||
}
|
||||
|
||||
return isPromotionMove
|
||||
}
|
||||
|
||||
func (p *Pawn) HandleEnPassant(b *Board, move, lastMove types.Move) bool {
|
||||
var wasEnPassant bool
|
||||
|
||||
if lastMove.PieceMoved != types.PawnShortName {
|
||||
return false
|
||||
}
|
||||
|
||||
switch move.ColorMoved {
|
||||
case types.White:
|
||||
if lastMove.StartSquare.Row != 7 || lastMove.EndSquare.Row != 5 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Row != 5 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.EndSquare.Row != 6 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
break
|
||||
}
|
||||
case types.Black:
|
||||
if lastMove.StartSquare.Row != 2 || lastMove.EndSquare.Row != 4 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Row != 4 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.EndSquare.Row != 3 {
|
||||
wasEnPassant = false
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
break
|
||||
}
|
||||
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||
wasEnPassant = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if wasEnPassant { //play the move
|
||||
delete(b.position, lastMove.EndSquare)
|
||||
b.position[move.EndSquare] = GetPieceForShortName(move.PieceMoved, move.ColorMoved)
|
||||
}
|
||||
|
||||
return wasEnPassant
|
||||
}
|
||||
func (p Pawn) getAllMoves(fromSquare types.Coordinate) []types.Coordinate {
|
||||
theoreticalMoves := make([]types.Coordinate, 0, 4)
|
||||
|
||||
@ -80,3 +176,4 @@ func (p Pawn) filterBlockedSquares(board Board, fromSquare types.Coordinate, squ
|
||||
}
|
||||
return lo.Intersect(nonBlockedSquares, squaresToBeFiltered)
|
||||
}
|
||||
|
||||
|
@ -3,17 +3,16 @@ package chess
|
||||
import "mchess_server/types"
|
||||
|
||||
type Rook struct {
|
||||
Color types.ChessColor
|
||||
Color types.ChessColor
|
||||
}
|
||||
|
||||
func (r Rook) AfterMoveAction() {
|
||||
}
|
||||
|
||||
// GetColor implements Piece.
|
||||
func (r Rook) GetColor() types.ChessColor {
|
||||
return r.Color
|
||||
}
|
||||
|
||||
func (r Rook) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return []types.Coordinate{}
|
||||
return board.GetNonBlockedRowAndColumn(fromSquare)
|
||||
}
|
||||
|
72
chess/rook_test.go
Normal file
72
chess/rook_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
package chess
|
||||
|
||||
import (
|
||||
"mchess_server/types"
|
||||
"testing"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Rook_GetNonBlockedSquares(t *testing.T) {
|
||||
t.Run("free row and column", func(t *testing.T) {
|
||||
board := newBoard()
|
||||
rook := Rook{Color: types.Black}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 5}] = rook
|
||||
squares := rook.GetAllMovesButBlocked(board, types.Coordinate{Col: 5, Row: 5})
|
||||
assert.Len(t, squares, 14)
|
||||
assert.Equal(t, types.Coordinate{Col: 4, Row: 5}, squares[0])
|
||||
})
|
||||
|
||||
t.Run("free row and column", func(t *testing.T) {
|
||||
board := newBoard()
|
||||
rook := Rook{Color: types.Black}
|
||||
rookCoordinate := types.Coordinate{Col: 5, Row: 5}
|
||||
|
||||
board.position[rookCoordinate] = rook
|
||||
board.position[types.Coordinate{Col: 3, Row: 5}] = Pawn{Color: types.White}
|
||||
board.position[types.Coordinate{Col: 5, Row: 6}] = Pawn{Color: types.White}
|
||||
board.position[types.Coordinate{Col: 6, Row: 5}] = Pawn{Color: types.Black}
|
||||
|
||||
squares := rook.GetAllMovesButBlocked(board, types.Coordinate{Col: 5, Row: 5})
|
||||
|
||||
squaresOnLeft := lo.Filter(squares, func(square types.Coordinate, _ int) bool {
|
||||
return square.Row == rookCoordinate.Row && square.Col < rookCoordinate.Col
|
||||
})
|
||||
assert.Equal(t,
|
||||
[]types.Coordinate{
|
||||
{Col: 4, Row: 5},
|
||||
{Col: 3, Row: 5},
|
||||
},
|
||||
squaresOnLeft)
|
||||
|
||||
squaresOnRight := lo.Filter(squares, func(square types.Coordinate, _ int) bool {
|
||||
return square.Row == rookCoordinate.Row && square.Col > rookCoordinate.Col
|
||||
})
|
||||
assert.Equal(t,
|
||||
[]types.Coordinate{},
|
||||
squaresOnRight)
|
||||
|
||||
squaresAbove := lo.Filter(squares, func(square types.Coordinate, _ int) bool {
|
||||
return square.Col == rookCoordinate.Col && square.Row > rookCoordinate.Row
|
||||
})
|
||||
assert.Equal(t,
|
||||
[]types.Coordinate{
|
||||
{Col: 5, Row: 6},
|
||||
},
|
||||
squaresAbove)
|
||||
|
||||
squaresBelow := lo.Filter(squares, func(square types.Coordinate, _ int) bool {
|
||||
return square.Col == rookCoordinate.Col && square.Row < rookCoordinate.Row
|
||||
})
|
||||
assert.Equal(t,
|
||||
[]types.Coordinate{
|
||||
{Col: 5, Row: 4},
|
||||
{Col: 5, Row: 3},
|
||||
{Col: 5, Row: 2},
|
||||
{Col: 5, Row: 1},
|
||||
},
|
||||
squaresBelow)
|
||||
})
|
||||
}
|
@ -1,49 +1,6 @@
|
||||
package types
|
||||
|
||||
// coordinates starting at 1:1 and end at 8:8
|
||||
type Coordinate struct {
|
||||
Col int `json:"col"`
|
||||
Row int `json:"row"`
|
||||
}
|
||||
|
||||
const (
|
||||
RangeLastValid = 8
|
||||
RangeFirstValid = 1
|
||||
|
||||
RangeUpperInvalid = 9
|
||||
RangeLowerInvalid = 0
|
||||
)
|
||||
|
||||
func (c Coordinate) Up(number int) *Coordinate {
|
||||
check := c.Row + number
|
||||
if check <= RangeLastValid {
|
||||
return &Coordinate{Row: check, Col: c.Col}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (c Coordinate) Down(number int) *Coordinate {
|
||||
check := c.Row - number
|
||||
if check >= RangeFirstValid {
|
||||
return &Coordinate{Row: check, Col: c.Col}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Right and left is seen from a board where row 1 is on the bottom
|
||||
func (c Coordinate) Right(number int) *Coordinate {
|
||||
check := c.Col + number
|
||||
if check >= RangeFirstValid {
|
||||
return &Coordinate{Row: c.Row, Col: check}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (c Coordinate) Left(number int) *Coordinate {
|
||||
check := c.Col - number
|
||||
if check >= RangeFirstValid {
|
||||
return &Coordinate{Row: c.Row, Col: check}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChessColor string
|
||||
|
||||
|
127
types/coordinate.go
Normal file
127
types/coordinate.go
Normal file
@ -0,0 +1,127 @@
|
||||
package types
|
||||
|
||||
// coordinates starting at 1:1 and end at 8:8
|
||||
type Coordinate struct {
|
||||
Col int `json:"col"`
|
||||
Row int `json:"row"`
|
||||
}
|
||||
|
||||
const (
|
||||
RangeLastValid = 8
|
||||
RangeFirstValid = 1
|
||||
|
||||
RangeUpperInvalid = 9
|
||||
RangeLowerInvalid = 0
|
||||
)
|
||||
|
||||
func (c Coordinate) Up(number int) *Coordinate {
|
||||
check := c.Row + number
|
||||
if check <= RangeLastValid {
|
||||
return &Coordinate{Row: check, Col: c.Col}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (c Coordinate) Down(number int) *Coordinate {
|
||||
check := c.Row - number
|
||||
if check >= RangeFirstValid {
|
||||
return &Coordinate{Row: check, Col: c.Col}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Right and left is seen from a board where row 1 is on the bottom
|
||||
func (c Coordinate) Right(number int) *Coordinate {
|
||||
check := c.Col + number
|
||||
if check <= RangeLastValid {
|
||||
return &Coordinate{Row: c.Row, Col: check}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (c Coordinate) Left(number int) *Coordinate {
|
||||
check := c.Col - number
|
||||
if check >= RangeFirstValid {
|
||||
return &Coordinate{Row: c.Row, Col: check}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Coordinate) GetAllSquaresLeft() []Coordinate {
|
||||
squares := []Coordinate{}
|
||||
i := 1
|
||||
for {
|
||||
if square := c.Left(i); square != nil {
|
||||
squares = append(squares, *square)
|
||||
i += 1
|
||||
} else {
|
||||
return squares
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c Coordinate) GetAllSquaresRight() []Coordinate {
|
||||
squares := []Coordinate{}
|
||||
i := 1
|
||||
for {
|
||||
if square := c.Right(i); square != nil {
|
||||
squares = append(squares, *square)
|
||||
i += 1
|
||||
} else {
|
||||
return squares
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c Coordinate) GetAllSquaresAbove() []Coordinate {
|
||||
squares := []Coordinate{}
|
||||
i := 1
|
||||
for {
|
||||
if square := c.Up(i); square != nil {
|
||||
squares = append(squares, *square)
|
||||
i += 1
|
||||
} else {
|
||||
return squares
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c Coordinate) GetAllSquaresBelow() []Coordinate {
|
||||
squares := []Coordinate{}
|
||||
i := 1
|
||||
for {
|
||||
if square := c.Down(i); square != nil {
|
||||
squares = append(squares, *square)
|
||||
i += 1
|
||||
} else {
|
||||
return squares
|
||||
}
|
||||
}
|
||||
}
|
||||
func (c Coordinate) GetSquaresOnRow() []Coordinate {
|
||||
rowToReturn := []Coordinate{}
|
||||
leftMostSquareOnRow := Coordinate{Col: RangeFirstValid, Row: c.Row}
|
||||
index := 0
|
||||
for {
|
||||
squareToAppend := leftMostSquareOnRow.Right(index)
|
||||
if squareToAppend == nil {
|
||||
break
|
||||
}
|
||||
rowToReturn = append(rowToReturn, *squareToAppend)
|
||||
index = index + 1
|
||||
}
|
||||
return rowToReturn
|
||||
}
|
||||
|
||||
func (c Coordinate) GetSquaresOnColumn() []Coordinate {
|
||||
columnToReturn := []Coordinate{}
|
||||
bottomSquareOnColumn := Coordinate{Col: c.Col, Row: RangeFirstValid}
|
||||
index := 0
|
||||
for {
|
||||
squareToAppend := bottomSquareOnColumn.Up(index)
|
||||
if squareToAppend == nil {
|
||||
break
|
||||
}
|
||||
columnToReturn = append(columnToReturn, *squareToAppend)
|
||||
index = index + 1
|
||||
}
|
||||
return columnToReturn
|
||||
}
|
25
types/coordinate_test.go
Normal file
25
types/coordinate_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Coordinate_GetSquaresOfRow(t *testing.T) {
|
||||
t.Run("get row for a coordinate", func(t *testing.T) {
|
||||
startSquare := Coordinate{Col: 4, Row: 2}
|
||||
row := startSquare.GetSquaresOnRow()
|
||||
assert.Len(t, row, 8)
|
||||
assert.Equal(t,row[0].Col, 1)
|
||||
assert.Equal(t,row[7].Col, 8)
|
||||
})
|
||||
|
||||
t.Run("get column for a coordinate", func(t *testing.T) {
|
||||
startSquare := Coordinate{Col: 4, Row: 2}
|
||||
column := startSquare.GetSquaresOnColumn()
|
||||
assert.Len(t, column, 8)
|
||||
assert.Equal(t,column[0].Row, 1)
|
||||
assert.Equal(t,column[7].Row, 8)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user