From f733d9bb08874597c6cb5f3d8727a493c068af91 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 27 Jun 2023 22:32:24 +0200 Subject: [PATCH] many many changes again. --- chess/bishop.go | 10 ++- chess/board.go | 43 ++++++----- chess/board_test.go | 14 ++-- chess/free_squares.go | 44 ++++++++---- chess/king.go | 12 +++- chess/knight.go | 12 ++-- chess/pawn.go | 68 ++++++++++-------- chess/piece_interface.go | 3 +- chess/queen.go | 14 +++- chess/rook.go | 12 +++- chess/rook_test.go | 5 +- types/coordinate.go | 149 +++++++++++++++++++++++---------------- types/coordinate_test.go | 43 +++++++---- 13 files changed, 269 insertions(+), 160 deletions(-) diff --git a/chess/bishop.go b/chess/bishop.go index 6fa5bbb..229b4b5 100644 --- a/chess/bishop.go +++ b/chess/bishop.go @@ -1,15 +1,21 @@ package chess -import "mchess_server/types" +import ( + "mchess_server/types" +) type Bishop struct { Color types.ChessColor } +func (b Bishop) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + return b.GetAllNonBlockedMoves(board, fromSquare) +} + func (b Bishop) GetColor() types.ChessColor { return b.Color } -func (b Bishop) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (b Bishop) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { return []types.Coordinate{} } diff --git a/chess/board.go b/chess/board.go index 3d2a375..4fbd43f 100644 --- a/chess/board.go +++ b/chess/board.go @@ -1,6 +1,7 @@ package chess import ( + "errors" "mchess_server/types" "github.com/samber/lo" @@ -78,7 +79,7 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, string) { if !wasSpecialMove { // At the moment, we do not need to check if the correct color is moving, //since we are only reading moves from the player whose turn it is. - allMovesExceptBlocked := pieceAtStartSquare.GetAllMovesButBlocked(tempBoard, move.StartSquare) + allMovesExceptBlocked := pieceAtStartSquare.GetAllNonBlockedMoves(tempBoard, move.StartSquare) legal := lo.Contains(allMovesExceptBlocked, move.EndSquare) if !legal { return false, "not a legal square" @@ -89,16 +90,12 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, string) { tempBoard.position[move.EndSquare] = pieceAtStartSquare } - //Check if king of moving color is in check -> move not allowed - //Do that by checking if the king is in a square attacked by the other color. - ownKingCoordinate := tempBoard.getSquareOfPiece(King{Color: move.ColorMoved}) - if ownKingCoordinate == nil { - return false, string(move.ColorMoved) + " king not found" + kingAttacked, err := tempBoard.isKingOfMovingColorInCheck(move.ColorMoved) + if err != nil { + return false, "something went wrong" } - - kingIsAttacked := tempBoard.isSquareAttacked(*ownKingCoordinate, move.ColorMoved.Opposite()) - if kingIsAttacked { - return false, "king is attacked after move" + if kingAttacked { + return false, "king would be in check after move" } //Check for checkmat&e @@ -118,10 +115,24 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, string) { b.position = tempBoard.position b.history = tempBoard.history b.appendMoveToHistory(move) - return true, "" } +func (b Board) isKingOfMovingColorInCheck(color types.ChessColor) (bool, error) { + //Check if king of moving color is in check -> move not allowed + //Do that by checking if the king is in a square attacked by the other color. + ownKingCoordinate := b.getSquareOfPiece(King{Color: color}) + if ownKingCoordinate == nil { + return false, errors.New("no king found") + } + + kingIsAttacked := b.isSquareAttacked(*ownKingCoordinate, color.Opposite()) + if kingIsAttacked { + return true, nil + } + return false, nil +} + func (b Board) getSquareOfPiece(piece Piece) *types.Coordinate { for k, v := range b.position { if v == piece { @@ -135,9 +146,10 @@ func (b Board) isSquareAttacked(square types.Coordinate, byColor types.ChessColo var attackedSquares []types.Coordinate for square, piece := range b.position { - attackedSquares = append(attackedSquares, piece.GetAllMovesButBlocked(b, square)...) + if piece.GetColor() == byColor { + attackedSquares = append(attackedSquares, piece.GetAllAttackedSquares(b, square)...) + } } - return lo.Contains(attackedSquares, square) } @@ -146,7 +158,6 @@ func (b Board) getPieceAt(coord types.Coordinate) Piece { if !found { return nil } - return piece } @@ -183,7 +194,7 @@ func (b *Board) handleSpecialMove(move types.Move) bool { switch pieceAtStartSquare.(type) { case Pawn: - pawn := pieceAtStartSquare.(Pawn) + pawn := pieceAtStartSquare.(Pawn) was = pawn.HandlePossiblePromotion(b, move) if !was { was = pawn.HandleEnPassant(b, move, b.getLastMove()) @@ -191,5 +202,3 @@ func (b *Board) handleSpecialMove(move types.Move) bool { } return was } - - diff --git a/chess/board_test.go b/chess/board_test.go index b6012aa..417dcef 100644 --- a/chess/board_test.go +++ b/chess/board_test.go @@ -122,14 +122,18 @@ func Test_CheckMove_invalidPawnMoves(t *testing.T) { 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} + board.position[types.Coordinate{Col: 7, Row: 6}] = Rook{Color: types.Black} + board.position[types.Coordinate{Col: 8, Row: 8}] = King{Color: types.Black} + boardBeforeMove := board move := types.Move{ - StartSquare: types.Coordinate{Col: 2, Row: 6}, - EndSquare: types.Coordinate{Col: 2, Row: 7}, - } + StartSquare: types.Coordinate{Col: 2, Row: 6}, + EndSquare: types.Coordinate{Col: 2, Row: 7}, + } good, _ := board.CheckAndPlay(move) + assert.False(t, good) + assert.Equal(t, boardBeforeMove, board) }) } @@ -139,7 +143,7 @@ func Test_CheckMove_validPromotion(t *testing.T) { board.position[types.Coordinate{Col: 1, Row: 7}] = Pawn{Color: types.White} board.position[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White} - board.position[types.Coordinate{Col: 8, Row: 7}] = King{Color: types.Black} + board.position[types.Coordinate{Col: 7, Row: 7}] = King{Color: types.Black} shortName := types.QueenShortName move := types.Move{ diff --git a/chess/free_squares.go b/chess/free_squares.go index a124e0c..852dd75 100644 --- a/chess/free_squares.go +++ b/chess/free_squares.go @@ -3,26 +3,44 @@ 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{} + squaresLeft := fromSquare.GetStraightInDirection(types.Coordinate.Left) + squaresRight := fromSquare.GetStraightInDirection(types.Coordinate.Right) + squaresAbove := fromSquare.GetStraightInDirection(types.Coordinate.Up) + squaresBelow := fromSquare.GetStraightInDirection(types.Coordinate.Down) - 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)...) + 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) GetNonBlockedDiagonals(fromSquare types.Coordinate) []types.Coordinate { + rightUp := fromSquare.GetDiagonalInDirection(types.Coordinate.Right, types.Coordinate.Up) + leftUp := fromSquare.GetDiagonalInDirection(types.Coordinate.Left, types.Coordinate.Up) + leftDown := fromSquare.GetDiagonalInDirection(types.Coordinate.Left, types.Coordinate.Down) + rightDown := fromSquare.GetDiagonalInDirection(types.Coordinate.Right, types.Coordinate.Down) + + nonBlocked := b.getNonBlocked(rightUp, fromSquare) + nonBlocked = append(nonBlocked, b.getNonBlocked(leftUp, fromSquare)...) + nonBlocked = append(nonBlocked, b.getNonBlocked(leftDown, fromSquare)...) + nonBlocked = append(nonBlocked, b.getNonBlocked(rightDown, fromSquare)...) + + return nonBlocked +} + +func (b *Board) GetNonBlockedKnightMoves(fromSquare types.Coordinate) []types.Coordinate { + allKnightMoves := fromSquare.GetAllKnightMoves() + return b.getNonBlocked(allKnightMoves, fromSquare) +} + func (b *Board) getNonBlocked( - squaresToCheck []types.Coordinate, - sourceSquare types.Coordinate, + squaresToCheck []types.Coordinate, + sourceSquare types.Coordinate, ) []types.Coordinate { pieceOnSourceSquare := b.getPieceAt(sourceSquare) - nonBlocked := []types.Coordinate{} + nonBlocked := []types.Coordinate{} for _, square := range squaresToCheck { piece := b.getPieceAt(square) @@ -37,5 +55,5 @@ func (b *Board) getNonBlocked( } nonBlocked = append(nonBlocked, square) } - return nonBlocked + return nonBlocked } diff --git a/chess/king.go b/chess/king.go index e4f3c82..a35bffb 100644 --- a/chess/king.go +++ b/chess/king.go @@ -1,15 +1,21 @@ package chess -import "mchess_server/types" +import ( + "mchess_server/types" +) type King struct { - Color types.ChessColor + Color types.ChessColor +} + +func (k King) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + return k.GetAllNonBlockedMoves(board, fromSquare) } func (k King) GetColor() types.ChessColor { return k.Color } -func (k King) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (k King) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { return []types.Coordinate{} } diff --git a/chess/knight.go b/chess/knight.go index f3e53c2..a8b4f7e 100644 --- a/chess/knight.go +++ b/chess/knight.go @@ -1,19 +1,21 @@ package chess -import "mchess_server/types" +import ( + "mchess_server/types" +) type Knight struct { Color types.ChessColor } -// AfterMoveAction implements Piece. -func (k Knight) AfterMoveAction() { +func (k Knight) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + return k.GetAllNonBlockedMoves(board, fromSquare) } func (k Knight) GetColor() types.ChessColor { return k.Color } -func (k Knight) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { - return []types.Coordinate{} +func (k Knight) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { + return board.GetNonBlockedKnightMoves(fromSquare) } diff --git a/chess/pawn.go b/chess/pawn.go index bf09b8c..50ef94f 100644 --- a/chess/pawn.go +++ b/chess/pawn.go @@ -10,7 +10,18 @@ type Pawn struct { Color types.ChessColor } -func (p Pawn) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { +func (p Pawn) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + attackingMoves := make([]types.Coordinate,0,2) + allMoves := p.GetAllNonBlockedMoves(board, fromSquare) + for _,move := range allMoves { + if move.Col != fromSquare.Col { + attackingMoves = append(attackingMoves, move) + } + } + return attackingMoves +} + +func (p Pawn) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { theoreticalSquares := p.getAllMoves(fromSquare) legalSquares := p.filterBlockedSquares(board, fromSquare, theoreticalSquares) @@ -21,37 +32,37 @@ 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 +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() + //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 - } + 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 - } + 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 - } - } + 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) - } + if isPromotionMove { + delete(b.position, move.StartSquare) + b.position[move.EndSquare] = GetPieceForShortName(promotionToPiece, move.ColorMoved) + } - return isPromotionMove + return isPromotionMove } func (p *Pawn) HandleEnPassant(b *Board, move, lastMove types.Move) bool { @@ -123,8 +134,8 @@ func (p Pawn) getAllMoves(fromSquare types.Coordinate) []types.Coordinate { switch p.Color { case types.Black: firstMove := fromSquare.Row == types.RangeLastValid-1 - - if fromSquare.Down(1) != nil { + + if fromSquare.Down(1) != nil { theoreticalMoves = append(theoreticalMoves, *fromSquare.Down(1)) } if firstMove && fromSquare.Down(2) != nil { @@ -176,4 +187,3 @@ func (p Pawn) filterBlockedSquares(board Board, fromSquare types.Coordinate, squ } return lo.Intersect(nonBlockedSquares, squaresToBeFiltered) } - diff --git a/chess/piece_interface.go b/chess/piece_interface.go index 24fbacc..1ebc753 100644 --- a/chess/piece_interface.go +++ b/chess/piece_interface.go @@ -5,7 +5,8 @@ import ( ) type Piece interface { - GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate + GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate + GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate GetColor() types.ChessColor } diff --git a/chess/queen.go b/chess/queen.go index c47ebd7..5fc8c4f 100644 --- a/chess/queen.go +++ b/chess/queen.go @@ -1,15 +1,23 @@ package chess -import "mchess_server/types" +import ( + "mchess_server/types" +) type Queen struct { Color types.ChessColor } +func (q Queen) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + return q.GetAllNonBlockedMoves(board, fromSquare) +} + func (q Queen) GetColor() types.ChessColor { return q.Color } -func (q Queen) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { - return []types.Coordinate{} +func (q Queen) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { + squares := board.GetNonBlockedRowAndColumn(fromSquare) + squares = append(squares, board.GetNonBlockedDiagonals(fromSquare)...) + return squares } diff --git a/chess/rook.go b/chess/rook.go index e9533ee..5f4ee42 100644 --- a/chess/rook.go +++ b/chess/rook.go @@ -1,11 +1,17 @@ package chess -import "mchess_server/types" +import ( + "mchess_server/types" +) type Rook struct { Color types.ChessColor } +func (r Rook) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate { + return r.GetAllNonBlockedMoves(board, fromSquare) +} + func (r Rook) AfterMoveAction() { } @@ -13,6 +19,6 @@ func (r Rook) GetColor() types.ChessColor { return r.Color } -func (r Rook) GetAllMovesButBlocked(board Board, fromSquare types.Coordinate) []types.Coordinate { - return board.GetNonBlockedRowAndColumn(fromSquare) +func (r Rook) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate { + return board.GetNonBlockedRowAndColumn(fromSquare) } diff --git a/chess/rook_test.go b/chess/rook_test.go index 9fbe142..0cf6766 100644 --- a/chess/rook_test.go +++ b/chess/rook_test.go @@ -14,9 +14,8 @@ func Test_Rook_GetNonBlockedSquares(t *testing.T) { rook := Rook{Color: types.Black} board.position[types.Coordinate{Col: 5, Row: 5}] = rook - squares := rook.GetAllMovesButBlocked(board, types.Coordinate{Col: 5, Row: 5}) + squares := rook.GetAllNonBlockedMoves(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) { @@ -29,7 +28,7 @@ func Test_Rook_GetNonBlockedSquares(t *testing.T) { 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}) + squares := rook.GetAllNonBlockedMoves(board, rookCoordinate) squaresOnLeft := lo.Filter(squares, func(square types.Coordinate, _ int) bool { return square.Row == rookCoordinate.Row && square.Col < rookCoordinate.Col diff --git a/types/coordinate.go b/types/coordinate.go index 9cb428e..17398d4 100644 --- a/types/coordinate.go +++ b/types/coordinate.go @@ -1,5 +1,7 @@ package types +import "github.com/samber/lo" + // coordinates starting at 1:1 and end at 8:8 type Coordinate struct { Col int `json:"col"` @@ -14,6 +16,65 @@ const ( RangeLowerInvalid = 0 ) +type CoordinateBuilder struct { + coordinate Coordinate +} + +func (b *CoordinateBuilder) Up(number int) *CoordinateBuilder { + b.coordinate.Row += number + return b +} + +func (b *CoordinateBuilder) Down(number int) *CoordinateBuilder { + b.coordinate.Row -= number + return b +} + +func (b *CoordinateBuilder) Left(number int) *CoordinateBuilder { + b.coordinate.Col -= number + return b +} + +func (b *CoordinateBuilder) Right(number int) *CoordinateBuilder { + b.coordinate.Col += number + return b +} + +func (b *CoordinateBuilder) Resolve() *Coordinate { + c := b.coordinate + if c.Row <= RangeLastValid && + c.Row >= RangeFirstValid && + c.Col <= RangeLastValid && + c.Col >= RangeLastValid { + return nil + } + + return &c +} + +func (c *Coordinate) GetAllKnightMoves() []Coordinate { + unfilteredMoves := make([]*Coordinate, 0, 8) + + builder := CoordinateBuilder{coordinate: *c} + + unfilteredMoves = append(unfilteredMoves, builder.Up(2).Right(1).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Up(1).Right(2).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Down(1).Right(2).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Down(2).Right(1).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Down(2).Left(1).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Down(1).Left(2).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Up(1).Left(2).Resolve()) + unfilteredMoves = append(unfilteredMoves, builder.Up(2).Left(1).Resolve()) + + + return lo.FilterMap(unfilteredMoves, func(unfilteredMove *Coordinate, _ int) (Coordinate, bool) { + if unfilteredMove != nil { + return *unfilteredMove, true + } + return Coordinate{}, false + }) +} + func (c Coordinate) Up(number int) *Coordinate { check := c.Row + number if check <= RangeLastValid { @@ -45,11 +106,27 @@ func (c Coordinate) Left(number int) *Coordinate { return nil } -func (c Coordinate) GetAllSquaresLeft() []Coordinate { +func (c Coordinate) GetAllSquaresOnStraights() []Coordinate { + squares := c.GetStraightInDirection(Coordinate.Up) + squares = append(squares, c.GetStraightInDirection(Coordinate.Down)...) + squares = append(squares, c.GetStraightInDirection(Coordinate.Left)...) + squares = append(squares, c.GetStraightInDirection(Coordinate.Right)...) + return squares +} + +func (c Coordinate) GetAllSquaresOnDiagonals() []Coordinate { + squares := c.GetDiagonalInDirection(Coordinate.Up, Coordinate.Right) + squares = append(squares, c.GetDiagonalInDirection(Coordinate.Down, Coordinate.Right)...) + squares = append(squares, c.GetDiagonalInDirection(Coordinate.Down, Coordinate.Left)...) + squares = append(squares, c.GetDiagonalInDirection(Coordinate.Up, Coordinate.Left)...) + return squares +} + +func (c Coordinate) GetStraightInDirection(dirFn func(Coordinate, int) *Coordinate) []Coordinate { squares := []Coordinate{} i := 1 for { - if square := c.Left(i); square != nil { + if square := dirFn(c, i); square != nil { squares = append(squares, *square) i += 1 } else { @@ -58,70 +135,20 @@ func (c Coordinate) GetAllSquaresLeft() []Coordinate { } } -func (c Coordinate) GetAllSquaresRight() []Coordinate { +func (c Coordinate) GetDiagonalInDirection(dirFn1, dirFn2 func(Coordinate, int) *Coordinate) []Coordinate { squares := []Coordinate{} i := 1 for { - if square := c.Right(i); square != nil { - squares = append(squares, *square) - i += 1 + if squareRight := dirFn1(c, i); squareRight != nil { + if square := dirFn2(*squareRight, i); square != nil { + squares = append(squares, *square) + i += 1 + } else { + break + } } 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 + return squares } diff --git a/types/coordinate_test.go b/types/coordinate_test.go index ebd0433..d913a0f 100644 --- a/types/coordinate_test.go +++ b/types/coordinate_test.go @@ -6,20 +6,33 @@ import ( "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) - }) +func Test_Coordinate_GetSquaresOfRowExcludingStartSquare(t *testing.T) { + t.Run("get row for a coordinate", func(t *testing.T) { + startSquare := Coordinate{Col: 4, Row: 2} + row := startSquare.GetStraightInDirection(Coordinate.Left) + row = append(row, startSquare.GetStraightInDirection(Coordinate.Right)...) + assert.Len(t, row, 7) + assert.Equal(t, row[0], Coordinate{Col: 3, Row: 2}) + assert.Equal(t, row[1], Coordinate{Col: 2, Row: 2}) + }) - 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) - }) + t.Run("get column for a coordinate", func(t *testing.T) { + startSquare := Coordinate{Col: 4, Row: 2} + column := startSquare.GetStraightInDirection(Coordinate.Up) + column = append(column, startSquare.GetStraightInDirection(Coordinate.Down)...) + assert.Len(t, column, 7) + assert.Equal(t, column[0], Coordinate{Col: 4, Row: 3}) + }) +} + +func Test_GetDiagonals(t *testing.T) { + square := Coordinate{Col: 5, Row: 5} + diagonal := square.GetDiagonalInDirection(Coordinate.Right, Coordinate.Up) + + assert.Len(t, diagonal, 3) + assert.Equal(t, []Coordinate{ + {Col: 6, Row: 6}, + {Col: 7, Row: 7}, + {Col: 8, Row: 8}, + }, diagonal) }