From 3a12a76a3f649a8b54d82ba759da8c1a3b8e6c25 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Mon, 16 Aug 2021 20:15:06 +0900 Subject: [PATCH 01/42] =?UTF-8?q?feat:=20=ED=94=BC=EC=8A=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 +++++++++++++++++++ build.gradle | 1 + src/main/java/chess/domain/piece/Bishop.java | 8 ++++ src/main/java/chess/domain/piece/Color.java | 6 +++ src/main/java/chess/domain/piece/King.java | 8 ++++ src/main/java/chess/domain/piece/Knight.java | 8 ++++ src/main/java/chess/domain/piece/Pawn.java | 8 ++++ src/main/java/chess/domain/piece/Piece.java | 15 +++++++ src/main/java/chess/domain/piece/Queen.java | 8 ++++ src/main/java/chess/domain/piece/Rook.java | 8 ++++ .../java/chess/domain/piece/PieceTest.java | 22 ++++++++++ src/test/java/empty.txt | 0 12 files changed, 135 insertions(+) create mode 100644 src/main/java/chess/domain/piece/Bishop.java create mode 100644 src/main/java/chess/domain/piece/Color.java create mode 100644 src/main/java/chess/domain/piece/King.java create mode 100644 src/main/java/chess/domain/piece/Knight.java create mode 100644 src/main/java/chess/domain/piece/Pawn.java create mode 100644 src/main/java/chess/domain/piece/Piece.java create mode 100644 src/main/java/chess/domain/piece/Queen.java create mode 100644 src/main/java/chess/domain/piece/Rook.java create mode 100644 src/test/java/chess/domain/piece/PieceTest.java delete mode 100644 src/test/java/empty.txt diff --git a/README.md b/README.md index 4a9cf0f..0a5fb11 100644 --- a/README.md +++ b/README.md @@ -1 +1,44 @@ # java-chess 게임 + +## 구현해야할 목록 + +- [] 입력 +- [] 출력 + +- [x] 기물(Piece) + - [x] 색상 + +- [] 체스판(Board) + - [] 체스판 + - [] 생성 시 32개의 기물 초기화 + +- [x] 기물 색상(Color) + - [x] 색상 + +- [] 파일(File) +- [] 랭크(Rank) + +- [] 체스판 : 8 X 8 + - 가로 ↠ : a ~ h + - 세로 ↟ : 1 ~ 8 + +## 이동 규칙 + +- [] 폰 (1 or 0.5) + - 적 방향 직선 1칸 이동 (처음 이동 시에는 2칸 이동 가능) + - 공격 : 적 방향 좌, 우 대각선 1칸 + +- [] 룩 (5) + - 모든 직선 방향으로 원하는 만큼 이동 가능 + +- [] 나이트 (2.5) + - 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 (진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.) + +- [] 비숍 (3) + - 모든 대각선 방향으로 원하는 만큼 이동 가능 + +- [] 퀸 (9) + - 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능) + +- [] 킹 + - 모든 방향 1칸 이동 (상대의 공격 범위로는 이동 불가능) diff --git a/build.gradle b/build.gradle index d41dd5b..50af46f 100644 --- a/build.gradle +++ b/build.gradle @@ -18,4 +18,5 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' testCompile "org.assertj:assertj-core:3.14.0" + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2' } diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java new file mode 100644 index 0000000..f18d082 --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class Bishop extends Piece { + + public Bishop(final Color color) { + super(color); + } +} diff --git a/src/main/java/chess/domain/piece/Color.java b/src/main/java/chess/domain/piece/Color.java new file mode 100644 index 0000000..517538f --- /dev/null +++ b/src/main/java/chess/domain/piece/Color.java @@ -0,0 +1,6 @@ +package chess.domain.piece; + +public enum Color { + WHITE, + BLACK +} diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java new file mode 100644 index 0000000..957ae20 --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class King extends Piece { + + public King(final Color color) { + super(color); + } +} diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java new file mode 100644 index 0000000..1536a29 --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class Knight extends Piece { + + public Knight(final Color color) { + super(color); + } +} diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java new file mode 100644 index 0000000..4f3b3fb --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class Pawn extends Piece { + + public Pawn(final Color color) { + super(color); + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java new file mode 100644 index 0000000..e7f862b --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,15 @@ +package chess.domain.piece; + +import static chess.domain.piece.Color.WHITE; + +public abstract class Piece { + private final Color color; + + Piece(final Color color) { + this.color = color; + } + + public boolean isWhite() { + return WHITE == color; + } +} diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java new file mode 100644 index 0000000..a331082 --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class Queen extends Piece { + + public Queen(final Color color) { + super(color); + } +} diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java new file mode 100644 index 0000000..d9eb8a6 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,8 @@ +package chess.domain.piece; + +public class Rook extends Piece { + + public Rook(final Color color) { + super(color); + } +} diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java new file mode 100644 index 0000000..cb607b1 --- /dev/null +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -0,0 +1,22 @@ +package chess.domain.piece; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PieceTest { + + + @ParameterizedTest + @CsvSource({"WHITE, true", "BLACK, false"}) + @DisplayName("색상을 인자로 받아 객체를 생성한다.") + void create(Color color, boolean expected) { + //given, when + Piece piece = new Pawn(color); + + //then + assertThat(piece.isWhite()).isEqualTo(expected); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29..0000000 From 667d30ad6ce11cff200976afeb57e0051275171e Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Mon, 16 Aug 2021 20:56:27 +0900 Subject: [PATCH 02/42] =?UTF-8?q?feat:=20=ED=94=BC=EC=8A=A4=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=BA=90=EC=8B=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +++---- src/main/java/chess/domain/board/File.java | 12 ++++++ .../java/chess/domain/board/Position.java | 38 ++++++++++++++++++ src/main/java/chess/domain/board/Rank.java | 23 +++++++++++ .../java/chess/domain/board/PositionTest.java | 40 +++++++++++++++++++ 5 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 src/main/java/chess/domain/board/File.java create mode 100644 src/main/java/chess/domain/board/Position.java create mode 100644 src/main/java/chess/domain/board/Rank.java create mode 100644 src/test/java/chess/domain/board/PositionTest.java diff --git a/README.md b/README.md index 0a5fb11..4a11cef 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,14 @@ - [x] 기물(Piece) - [x] 색상 +- [x] 기물 위치(Position) + - [x] 파일 + - [x] 랭크 + - [x] 64개의 캐싱된 위치 + + - 기능 + - [x] 전달받은 인자에 해당하는 위치 객체 반환 + - [] 체스판(Board) - [] 체스판 - [] 생성 시 32개의 기물 초기화 @@ -15,13 +23,6 @@ - [x] 기물 색상(Color) - [x] 색상 -- [] 파일(File) -- [] 랭크(Rank) - -- [] 체스판 : 8 X 8 - - 가로 ↠ : a ~ h - - 세로 ↟ : 1 ~ 8 - ## 이동 규칙 - [] 폰 (1 or 0.5) diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java new file mode 100644 index 0000000..ef33fa5 --- /dev/null +++ b/src/main/java/chess/domain/board/File.java @@ -0,0 +1,12 @@ +package chess.domain.board; + +public enum File { + a, + b, + c, + d, + e, + f, + g, + h +} diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java new file mode 100644 index 0000000..c31cc1e --- /dev/null +++ b/src/main/java/chess/domain/board/Position.java @@ -0,0 +1,38 @@ +package chess.domain.board; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class Position { + private static final Map POSITIONS = createPositions(); + + private static Map createPositions() { + Map positions = new HashMap<>(); + + Arrays.stream(File.values()) + .forEach(file -> Arrays.stream(Rank.values()).forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); + + return positions; + } + + private static String createKey(final File file, final Rank rank) { + return file.name() + rank.getRank(); + } + + public static Position from(final String key) { + return POSITIONS.get(key); + } + + public static Position from(final File file, final Rank rank) { + return POSITIONS.get(createKey(file, rank)); + } + + private final File file; + private final Rank rank; + + private Position(final File file, final Rank rank) { + this.file = file; + this.rank = rank; + } +} diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java new file mode 100644 index 0000000..33a7cfb --- /dev/null +++ b/src/main/java/chess/domain/board/Rank.java @@ -0,0 +1,23 @@ +package chess.domain.board; + +public enum Rank { + R1(1), + R2(2), + R3(3), + R4(4), + R5(5), + R6(6), + R7(7), + R8(8); + + private final int rank; + + Rank(final int rank) { + this.rank = rank; + } + + public int getRank() { + return rank; + } +} + diff --git a/src/test/java/chess/domain/board/PositionTest.java b/src/test/java/chess/domain/board/PositionTest.java new file mode 100644 index 0000000..44604d4 --- /dev/null +++ b/src/test/java/chess/domain/board/PositionTest.java @@ -0,0 +1,40 @@ +package chess.domain.board; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PositionTest { + + @Test + @DisplayName("가로, 세로 인자에 해당하는 위치를 반환한다.") + void from_file_and_rank() { + //given + File file = File.a; + Rank rank = Rank.R1; + + //when + Position position = Position.from(file, rank); + + //then + assertThat(position).extracting("file", "rank") + .containsOnly(file, rank); + } + + @Test + @DisplayName("문자열 키에 해당하는 위치를 반환한다.") + void from_key() { + //given + File file = File.a; + Rank rank = Rank.R1; + String key = file.name() + rank.getRank(); + + //when + Position position = Position.from(key); + + //then + assertThat(position).extracting("file", "rank") + .containsOnly(file, rank); + } +} From 05e7c1de96dcb4adec282a4cef0f750db0745948 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Mon, 16 Aug 2021 21:45:42 +0900 Subject: [PATCH 03/42] =?UTF-8?q?feat:=20=EC=B2=B4=EC=8A=A4=ED=8C=90=2032?= =?UTF-8?q?=EA=B0=9C=EC=9D=98=20=ED=94=BC=EC=8A=A4=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94,=20=EC=83=9D=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +-- src/main/java/chess/domain/board/Board.java | 47 +++++++++++++++++++ .../java/chess/domain/board/BoardTest.java | 24 ++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/main/java/chess/domain/board/Board.java create mode 100644 src/test/java/chess/domain/board/BoardTest.java diff --git a/README.md b/README.md index 4a11cef..3353994 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,10 @@ - 기능 - [x] 전달받은 인자에 해당하는 위치 객체 반환 -- [] 체스판(Board) - - [] 체스판 - - [] 생성 시 32개의 기물 초기화 +- [x] 체스판(Board) + - [x] 체스판 + - [x] 생성 시 32개의 기물 초기화 + - [x] 인자로 전달받은 위치에 기물이 있는지 확인 - [x] 기물 색상(Color) - [x] 색상 diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java new file mode 100644 index 0000000..3c85ecc --- /dev/null +++ b/src/main/java/chess/domain/board/Board.java @@ -0,0 +1,47 @@ +package chess.domain.board; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Color; +import chess.domain.piece.King; +import chess.domain.piece.Knight; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class Board { + private final Map board = new HashMap<>(); + + public Board() { + initializePawns(); + initializeOthers(Rank.R1, Color.WHITE); + initializeOthers(Rank.R8, Color.BLACK); + } + + private void initializeOthers(Rank rank, Color color) { + board.put(Position.from(File.a, rank), new Rook(color)); + board.put(Position.from(File.b, rank), new Knight(color)); + board.put(Position.from(File.c, rank), new Bishop(color)); + board.put(Position.from(File.d, rank), new Queen(color)); + board.put(Position.from(File.e, rank), new King(color)); + board.put(Position.from(File.f, rank), new Bishop(color)); + board.put(Position.from(File.g, rank), new Knight(color)); + board.put(Position.from(File.h, rank), new Rook(color)); + } + + private void initializePawns() { + Arrays.stream(File.values()) + .forEach(file -> { + board.put(Position.from(file, Rank.R2), new Pawn(Color.WHITE)); + board.put(Position.from(file, Rank.R7), new Pawn(Color.BLACK)); + }); + } + + public boolean isEmpty(Position position) { + return board.get(position) == null; + } +} diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java new file mode 100644 index 0000000..2cbcee9 --- /dev/null +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -0,0 +1,24 @@ +package chess.domain.board; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class BoardTest { + + @ParameterizedTest + @CsvSource({"a1, false", "a3, true"}) + @DisplayName("객체를 생성한다.") + void create(String key, boolean expected) { + //given + Position position = Position.from(key); + + //when + Board board = new Board(); + + //then + assertThat(board.isEmpty(position)).isEqualTo(expected); + } +} From 2d3380301a493e38509dcd9270e661aff366e982 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 15:03:26 +0900 Subject: [PATCH 04/42] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EC=A2=85?= =?UTF-8?q?=EB=A5=98=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EC=9D=84=20=EB=A7=A4=ED=95=91=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=AC=BC=20=EC=9D=B4=EB=A6=84=20=EB=A7=A4=ED=8D=BC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++-- .../domain/piece/mapper/BishopMapper.java | 18 +++++++++ .../chess/domain/piece/mapper/KingMapper.java | 18 +++++++++ .../domain/piece/mapper/KnightMapper.java | 18 +++++++++ .../chess/domain/piece/mapper/PawnMapper.java | 18 +++++++++ .../domain/piece/mapper/PieceMapper.java | 21 ++++++++++ .../domain/piece/mapper/PieceMappers.java | 38 +++++++++++++++++++ .../domain/piece/mapper/QueenMapper.java | 18 +++++++++ .../chess/domain/piece/mapper/RookMapper.java | 18 +++++++++ .../domain/piece/mapper/PieceMappersTest.java | 35 +++++++++++++++++ 10 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 src/main/java/chess/domain/piece/mapper/BishopMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/KingMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/KnightMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/PawnMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/PieceMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/PieceMappers.java create mode 100644 src/main/java/chess/domain/piece/mapper/QueenMapper.java create mode 100644 src/main/java/chess/domain/piece/mapper/RookMapper.java create mode 100644 src/test/java/chess/domain/piece/mapper/PieceMappersTest.java diff --git a/README.md b/README.md index 3353994..3f45505 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,18 @@ - 기능 - [x] 전달받은 인자에 해당하는 위치 객체 반환 +- [x] 기물 색상(Color) + - [x] 색상 + +- [x] 기물 이름 매퍼 + - [x] 기물 종류에 따라 이름을 매핑 + + - [x] 체스판(Board) - [x] 체스판 - [x] 생성 시 32개의 기물 초기화 - [x] 인자로 전달받은 위치에 기물이 있는지 확인 -- [x] 기물 색상(Color) - - [x] 색상 - ## 이동 규칙 - [] 폰 (1 or 0.5) diff --git a/src/main/java/chess/domain/piece/mapper/BishopMapper.java b/src/main/java/chess/domain/piece/mapper/BishopMapper.java new file mode 100644 index 0000000..f7512df --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/BishopMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Piece; + +public class BishopMapper extends PieceMapper { + + private static final String NAME = "b"; + + public BishopMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof Bishop; + } +} diff --git a/src/main/java/chess/domain/piece/mapper/KingMapper.java b/src/main/java/chess/domain/piece/mapper/KingMapper.java new file mode 100644 index 0000000..9b38e06 --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/KingMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.King; +import chess.domain.piece.Piece; + +public class KingMapper extends PieceMapper { + + private static final String NAME = "k"; + + public KingMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof King; + } +} diff --git a/src/main/java/chess/domain/piece/mapper/KnightMapper.java b/src/main/java/chess/domain/piece/mapper/KnightMapper.java new file mode 100644 index 0000000..af07be6 --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/KnightMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Knight; +import chess.domain.piece.Piece; + +public class KnightMapper extends PieceMapper { + + private static final String NAME = "n"; + + public KnightMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof Knight; + } +} diff --git a/src/main/java/chess/domain/piece/mapper/PawnMapper.java b/src/main/java/chess/domain/piece/mapper/PawnMapper.java new file mode 100644 index 0000000..7b416aa --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/PawnMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; + +public class PawnMapper extends PieceMapper { + + private static final String NAME = "p"; + + public PawnMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof Pawn; + } +} diff --git a/src/main/java/chess/domain/piece/mapper/PieceMapper.java b/src/main/java/chess/domain/piece/mapper/PieceMapper.java new file mode 100644 index 0000000..1880424 --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/PieceMapper.java @@ -0,0 +1,21 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Piece; + +public abstract class PieceMapper { + private final String name; + + PieceMapper(final String name) { + this.name = name; + } + + abstract boolean supports(Piece piece); + + public String findNameBy(Piece piece) { + if (piece.isWhite()) { + return name; + } + + return name.toUpperCase(); + } +} diff --git a/src/main/java/chess/domain/piece/mapper/PieceMappers.java b/src/main/java/chess/domain/piece/mapper/PieceMappers.java new file mode 100644 index 0000000..be4d012 --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/PieceMappers.java @@ -0,0 +1,38 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Piece; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.NoSuchElementException; + +public class PieceMappers { + + private static final Collection pieceMappers = createMappers(); + + private static Collection createMappers() { + return Collections.unmodifiableCollection( + Arrays.asList( + new PawnMapper(), + new KnightMapper(), + new BishopMapper(), + new RookMapper(), + new QueenMapper(), + new KingMapper() + )); + } + + public static String findNameBy(final Piece piece) { + return pieceMappers.stream() + .filter(mapper -> mapper.supports(piece)) + .map(mapper -> mapper.findNameBy(piece)) + .findAny() + .orElseThrow(() -> { + throw new NoSuchElementException("해당 기물에 해당하는 매퍼가 존재하지 않습니다."); + }); + } + + private PieceMappers() { + } +} diff --git a/src/main/java/chess/domain/piece/mapper/QueenMapper.java b/src/main/java/chess/domain/piece/mapper/QueenMapper.java new file mode 100644 index 0000000..bf20c2a --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/QueenMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Piece; +import chess.domain.piece.Queen; + +public class QueenMapper extends PieceMapper { + + private static final String NAME = "q"; + + public QueenMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof Queen; + } +} diff --git a/src/main/java/chess/domain/piece/mapper/RookMapper.java b/src/main/java/chess/domain/piece/mapper/RookMapper.java new file mode 100644 index 0000000..9f38c6d --- /dev/null +++ b/src/main/java/chess/domain/piece/mapper/RookMapper.java @@ -0,0 +1,18 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Piece; +import chess.domain.piece.Rook; + +public class RookMapper extends PieceMapper { + + private static final String NAME = "r"; + + public RookMapper() { + super(NAME); + } + + @Override + public boolean supports(final Piece piece) { + return piece instanceof Rook; + } +} diff --git a/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java b/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java new file mode 100644 index 0000000..6582ff8 --- /dev/null +++ b/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java @@ -0,0 +1,35 @@ +package chess.domain.piece.mapper; + +import chess.domain.piece.Color; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PieceMappersTest { + + @ParameterizedTest + @MethodSource("createParams") + @DisplayName("피스가 주어지면 해당 피스와 색상에 맞는 이름을 반환한다.") + void find(Piece piece, String expected) { + //given, when + String name = PieceMappers.findNameBy(piece); + + //then + assertThat(name).isEqualTo(expected); + + } + + private static Stream createParams() { + return Stream.of( + Arguments.of(new Pawn(Color.WHITE), "p"), + Arguments.of(new Pawn(Color.BLACK), "P") + ); + } +} From cc544923f6fd0b7b7c87b2a202b854a497fb9fd2 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 15:04:37 +0900 Subject: [PATCH 05/42] =?UTF-8?q?feat:=20=EC=B2=B4=EC=8A=A4=ED=8C=90=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + src/main/java/chess/ConsoleApplication.java | 14 +++++++ .../chess/controller/ChessController.java | 20 +++++++++ .../java/chess/controller/dto/BoardDto.java | 41 +++++++++++++++++++ .../chess/controller/dto/PositionDto.java | 28 +++++++++++++ src/main/java/chess/domain/board/Board.java | 13 ++++++ src/main/java/chess/domain/board/File.java | 26 ++++++++---- .../java/chess/domain/board/Position.java | 10 +++-- src/main/java/chess/domain/board/Rank.java | 26 ++++++------ .../java/chess/view/ConsoleOutputView.java | 18 ++++++++ src/main/java/chess/view/OutputView.java | 7 ++++ .../java/chess/domain/board/PositionTest.java | 2 +- 12 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 src/main/java/chess/ConsoleApplication.java create mode 100644 src/main/java/chess/controller/ChessController.java create mode 100644 src/main/java/chess/controller/dto/BoardDto.java create mode 100644 src/main/java/chess/controller/dto/PositionDto.java create mode 100644 src/main/java/chess/view/ConsoleOutputView.java create mode 100644 src/main/java/chess/view/OutputView.java diff --git a/README.md b/README.md index 3f45505..cefdbd6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - [] 입력 - [] 출력 + - [x] 체스판 전체 출력 - [x] 기물(Piece) - [x] 색상 diff --git a/src/main/java/chess/ConsoleApplication.java b/src/main/java/chess/ConsoleApplication.java new file mode 100644 index 0000000..a220824 --- /dev/null +++ b/src/main/java/chess/ConsoleApplication.java @@ -0,0 +1,14 @@ +package chess; + +import chess.controller.ChessController; +import chess.view.ConsoleOutputView; +import chess.view.OutputView; + +public class ConsoleApplication { + + public static void main(String[] args) { + OutputView outputView = new ConsoleOutputView(); + ChessController chessController = new ChessController(outputView); + chessController.run(); + } +} diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java new file mode 100644 index 0000000..2d57135 --- /dev/null +++ b/src/main/java/chess/controller/ChessController.java @@ -0,0 +1,20 @@ +package chess.controller; + +import chess.controller.dto.BoardDto; +import chess.domain.board.Board; +import chess.view.OutputView; + +public class ChessController { + + private final OutputView outputView; + + public ChessController(final OutputView outputView) { + this.outputView = outputView; + } + + public void run() { + Board board = new Board(); + BoardDto boardDto = new BoardDto(board); + outputView.printBoard(boardDto); + } +} diff --git a/src/main/java/chess/controller/dto/BoardDto.java b/src/main/java/chess/controller/dto/BoardDto.java new file mode 100644 index 0000000..31dd52c --- /dev/null +++ b/src/main/java/chess/controller/dto/BoardDto.java @@ -0,0 +1,41 @@ +package chess.controller.dto; + +import chess.domain.board.Board; +import chess.domain.board.File; +import chess.domain.board.Position; +import chess.domain.board.Rank; +import chess.domain.piece.Piece; +import chess.domain.piece.mapper.PieceMappers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class BoardDto { + private List positionDtos = new ArrayList<>(); + + public BoardDto(final Board board) { + Arrays.stream(Rank.values()).forEach(rank -> Arrays.stream( + File.values()).forEach(file -> addPositionDto(file, rank, board))); + } + + private void addPositionDto(final File file, final Rank rank, final Board board) { + Position position = Position.from(file, rank); + + if (board.isEmpty(position)) { + PositionDto positionDto = new PositionDto(position.getFile()); + positionDtos.add(positionDto); + return; + } + + Piece piece = board.findBy(position); + String name = PieceMappers.findNameBy(piece); + PositionDto positionDto = new PositionDto(position.getFile(), name); + positionDtos.add(positionDto); + } + + public List getPositionDtos() { + return Collections.unmodifiableList(positionDtos); + } +} diff --git a/src/main/java/chess/controller/dto/PositionDto.java b/src/main/java/chess/controller/dto/PositionDto.java new file mode 100644 index 0000000..384a91d --- /dev/null +++ b/src/main/java/chess/controller/dto/PositionDto.java @@ -0,0 +1,28 @@ +package chess.controller.dto; + +import chess.domain.board.File; + +public class PositionDto { + + private static final String DEFAULT_NAME = "."; + + private final boolean isLastFile; + private final String name; + + public PositionDto(final File file, final String name) { + this.isLastFile = (file == File.h); + this.name = name; + } + + public PositionDto(final File file) { + this(file, DEFAULT_NAME); + } + + public boolean isLastFile() { + return isLastFile; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index 3c85ecc..d099359 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -10,6 +10,7 @@ import chess.domain.piece.Rook; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -44,4 +45,16 @@ private void initializePawns() { public boolean isEmpty(Position position) { return board.get(position) == null; } + + public Piece findBy(final Position position) { + return board.get(position); + } + + public Collection getAllPresentPieces() { + return board.values(); + } + + public Collection getAllPresentPositions() { + return board.keySet(); + } } diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java index ef33fa5..c30eed7 100644 --- a/src/main/java/chess/domain/board/File.java +++ b/src/main/java/chess/domain/board/File.java @@ -1,12 +1,22 @@ package chess.domain.board; public enum File { - a, - b, - c, - d, - e, - f, - g, - h + a(1), + b(2), + c(3), + d(4), + e(5), + f(6), + g(7), + h(8); + + private final int index; + + File(final int index) { + this.index = index; + } + + public int getIndex() { + return index; + } } diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index c31cc1e..8a2be56 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -1,14 +1,14 @@ package chess.domain.board; import java.util.Arrays; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; public class Position { private static final Map POSITIONS = createPositions(); private static Map createPositions() { - Map positions = new HashMap<>(); + Map positions = new LinkedHashMap<>(); Arrays.stream(File.values()) .forEach(file -> Arrays.stream(Rank.values()).forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); @@ -17,7 +17,7 @@ private static Map createPositions() { } private static String createKey(final File file, final Rank rank) { - return file.name() + rank.getRank(); + return file.name() + rank.getIndex(); } public static Position from(final String key) { @@ -35,4 +35,8 @@ private Position(final File file, final Rank rank) { this.file = file; this.rank = rank; } + + public File getFile() { + return file; + } } diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java index 33a7cfb..781d032 100644 --- a/src/main/java/chess/domain/board/Rank.java +++ b/src/main/java/chess/domain/board/Rank.java @@ -1,23 +1,25 @@ package chess.domain.board; public enum Rank { - R1(1), - R2(2), - R3(3), - R4(4), - R5(5), - R6(6), + R8(8), R7(7), - R8(8); + R6(6), + R5(5), + R4(4), + R3(3), + R2(2), + R1(1); - private final int rank; + private final int index; - Rank(final int rank) { - this.rank = rank; + Rank(final int index) { + this.index = index; } - public int getRank() { - return rank; + public int getIndex() { + return index; } + + } diff --git a/src/main/java/chess/view/ConsoleOutputView.java b/src/main/java/chess/view/ConsoleOutputView.java new file mode 100644 index 0000000..8d08771 --- /dev/null +++ b/src/main/java/chess/view/ConsoleOutputView.java @@ -0,0 +1,18 @@ +package chess.view; + +import chess.controller.dto.BoardDto; + +public class ConsoleOutputView implements OutputView { + + + @Override + public void printBoard(final BoardDto boardDto) { + boardDto.getPositionDtos() + .forEach(positionDto -> { + System.out.print(positionDto.getName()); + if (positionDto.isLastFile()) { + System.out.println(); + } + }); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 0000000..1040482 --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,7 @@ +package chess.view; + +import chess.controller.dto.BoardDto; + +public interface OutputView { + void printBoard(BoardDto boardDto); +} diff --git a/src/test/java/chess/domain/board/PositionTest.java b/src/test/java/chess/domain/board/PositionTest.java index 44604d4..ad1ef0a 100644 --- a/src/test/java/chess/domain/board/PositionTest.java +++ b/src/test/java/chess/domain/board/PositionTest.java @@ -28,7 +28,7 @@ void from_key() { //given File file = File.a; Rank rank = Rank.R1; - String key = file.name() + rank.getRank(); + String key = file.name() + rank.getIndex(); //when Position position = Position.from(key); From 8bc0bd5b409f56e0c9347efa37fdcf2c67bc98bf Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 15:46:16 +0900 Subject: [PATCH 06/42] =?UTF-8?q?feat:=20=EC=B2=B4=EC=8A=A4=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=8B=9C=EC=9E=91&=EC=A2=85=EB=A3=8C=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=20=EC=9E=85=EB=A0=A5=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +++++++++------ src/main/java/chess/ConsoleApplication.java | 5 +- .../chess/controller/ChessController.java | 17 +++++-- src/main/java/chess/domain/ChessGame.java | 34 ++++++++++++++ .../java/chess/view/ConsoleInputView.java | 23 ++++++++++ src/main/java/chess/view/InputView.java | 5 ++ src/test/java/chess/domain/ChessGameTest.java | 46 +++++++++++++++++++ 7 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 src/main/java/chess/domain/ChessGame.java create mode 100644 src/main/java/chess/view/ConsoleInputView.java create mode 100644 src/main/java/chess/view/InputView.java create mode 100644 src/test/java/chess/domain/ChessGameTest.java diff --git a/README.md b/README.md index cefdbd6..73f18df 100644 --- a/README.md +++ b/README.md @@ -3,31 +3,38 @@ ## 구현해야할 목록 - [] 입력 + - [x] 시작 여부 입력 - [] 출력 - [x] 체스판 전체 출력 -- [x] 기물(Piece) - - [x] 색상 +- [ ] 체스게임(ChessGame) + - [x] 체스판 + - [x] 게임 시작&종료 상태 제어 + - [x] 명령어 검증 및 처리 -- [x] 기물 위치(Position) - - [x] 파일 - - [x] 랭크 - - [x] 64개의 캐싱된 위치 +- [x] 체스판(Board) + - [x] 체스판 + - [x] 생성 시 32개의 기물 초기화 + - [x] 인자로 전달받은 위치에 기물이 있는지 확인 - - 기능 - - [x] 전달받은 인자에 해당하는 위치 객체 반환 -- [x] 기물 색상(Color) +- [x] 기물(Piece) - [x] 색상 -- [x] 기물 이름 매퍼 - - [x] 기물 종류에 따라 이름을 매핑 + - [x] 기물 위치(Position) + - [x] 파일 + - [x] 랭크 + - [x] 64개의 캐싱된 위치 + - 기능 + - [x] 전달받은 인자에 해당하는 위치 객체 반환 + + - [x] 기물 색상(Color) + - [x] 색상 + + - [x] 기물 이름 매퍼 + - [x] 기물 종류에 따라 이름을 매핑 -- [x] 체스판(Board) - - [x] 체스판 - - [x] 생성 시 32개의 기물 초기화 - - [x] 인자로 전달받은 위치에 기물이 있는지 확인 ## 이동 규칙 diff --git a/src/main/java/chess/ConsoleApplication.java b/src/main/java/chess/ConsoleApplication.java index a220824..d5f97f2 100644 --- a/src/main/java/chess/ConsoleApplication.java +++ b/src/main/java/chess/ConsoleApplication.java @@ -1,14 +1,17 @@ package chess; import chess.controller.ChessController; +import chess.view.ConsoleInputView; import chess.view.ConsoleOutputView; +import chess.view.InputView; import chess.view.OutputView; public class ConsoleApplication { public static void main(String[] args) { OutputView outputView = new ConsoleOutputView(); - ChessController chessController = new ChessController(outputView); + InputView inputView = new ConsoleInputView(); + ChessController chessController = new ChessController(inputView, outputView); chessController.run(); } } diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index 2d57135..0c0d25b 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -1,20 +1,27 @@ package chess.controller; import chess.controller.dto.BoardDto; -import chess.domain.board.Board; +import chess.domain.ChessGame; +import chess.view.InputView; import chess.view.OutputView; public class ChessController { + private final InputView inputView; private final OutputView outputView; - public ChessController(final OutputView outputView) { + public ChessController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; this.outputView = outputView; } public void run() { - Board board = new Board(); - BoardDto boardDto = new BoardDto(board); - outputView.printBoard(boardDto); + String initialCommand = inputView.getInitialCommand(); + ChessGame chessGame = new ChessGame(initialCommand); + + while (chessGame.isRunning()) { + BoardDto boardDto = new BoardDto(chessGame.getBoard()); + outputView.printBoard(boardDto); + } } } diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java new file mode 100644 index 0000000..d426698 --- /dev/null +++ b/src/main/java/chess/domain/ChessGame.java @@ -0,0 +1,34 @@ +package chess.domain; + +import chess.domain.board.Board; + +public class ChessGame { + + private static final String START = "start"; + private static final String END = "end"; + + private final Board board = new Board(); + private boolean isRunning = false; + + public ChessGame(final String initialCommand) { + if (initialCommand.equals(START)) { + isRunning = true; + return; + } + + if (initialCommand.equals(END)) { + return; + } + + throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); + } + + + public boolean isRunning() { + return isRunning; + } + + public Board getBoard() { + return board; + } +} diff --git a/src/main/java/chess/view/ConsoleInputView.java b/src/main/java/chess/view/ConsoleInputView.java new file mode 100644 index 0000000..1438cf8 --- /dev/null +++ b/src/main/java/chess/view/ConsoleInputView.java @@ -0,0 +1,23 @@ +package chess.view; + +import java.util.Scanner; + +public class ConsoleInputView implements InputView { + private static final Scanner scanner = new Scanner(System.in); + private static final String HEADER = "> "; + + @Override + public String getInitialCommand() { + System.out.println(HEADER + "체스 게임을 시작합니다."); + System.out.println(HEADER + "게임 시작은 start, 종료는 end 명령을 입력하세요."); + String input = scanner.nextLine(); + validateNull(input); + return input.trim(); + } + + private void validateNull(final String input) { + if (input == null) { + throw new IllegalArgumentException("잘못된 입력입니다."); + } + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 0000000..8b5bfc9 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,5 @@ +package chess.view; + +public interface InputView { + String getInitialCommand(); +} diff --git a/src/test/java/chess/domain/ChessGameTest.java b/src/test/java/chess/domain/ChessGameTest.java new file mode 100644 index 0000000..891110c --- /dev/null +++ b/src/test/java/chess/domain/ChessGameTest.java @@ -0,0 +1,46 @@ +package chess.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ChessGameTest { + + @Test + @DisplayName("게임 시작 명령어를 입력받을 경우 게임을 시작한다.") + void create_start_command() { + //given + String command = "start"; + + //when + ChessGame chessGame = new ChessGame(command); + + //then + assertThat(chessGame.isRunning()).isTrue(); + } + + @Test + @DisplayName("게임 종료 명령어를 입력받을 경우 게임을 종료한다.") + void create_end_command() { + //given + String command = "end"; + + //when + ChessGame chessGame = new ChessGame(command); + + //then + assertThat(chessGame.isRunning()).isFalse(); + } + + @Test + @DisplayName("유효하지 않은 명령어를 입력받을 경우 예외를 던진다.") + void create_invalid_command() { + //given + String command = "invalid_command"; + + //when, then + assertThrows(UnsupportedOperationException.class, () -> new ChessGame(command)); + } +} From f6c2eb47bb7e440f4cc5d001a88aa71c166afc11 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 17:24:11 +0900 Subject: [PATCH 07/42] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EC=8B=9C=20=EA=B3=B5=ED=86=B5=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 ++++++++- .../chess/controller/ChessController.java | 6 ++- src/main/java/chess/domain/ChessGame.java | 26 +++++++--- src/main/java/chess/domain/board/Board.java | 36 +++++++++---- .../java/chess/domain/command/Command.java | 52 +++++++++++++++++++ .../chess/domain/command/MoveParameters.java | 31 +++++++++++ src/main/java/chess/domain/piece/Piece.java | 4 ++ .../java/chess/view/ConsoleInputView.java | 2 +- src/main/java/chess/view/InputView.java | 2 +- src/test/java/chess/domain/ChessGameTest.java | 22 ++++---- .../java/chess/domain/board/BoardTest.java | 42 +++++++++++++++ 11 files changed, 216 insertions(+), 32 deletions(-) create mode 100644 src/main/java/chess/domain/command/Command.java create mode 100644 src/main/java/chess/domain/command/MoveParameters.java diff --git a/README.md b/README.md index 73f18df..1e86af3 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,28 @@ - [ ] 체스게임(ChessGame) - [x] 체스판 + - [] 현재 플레이어 - [x] 게임 시작&종료 상태 제어 - - [x] 명령어 검증 및 처리 + + - 기능 + - [x] 명령어 검증 및 처리 + - [x] start : 게임 실행 + - [x] end : 게임 종료 + - [] move : 인자로 전달받은 source 위치에서 target 위치로 기물 이동 - [x] 체스판(Board) - [x] 체스판 - [x] 생성 시 32개의 기물 초기화 - - [x] 인자로 전달받은 위치에 기물이 있는지 확인 + - 기능 + - [x] 인자로 전달받은 위치에 기물이 있는지 확인 + - [x] ERROR : 존재하지 않을 경우 + - [x] 같은 색상 기물인지 확인 + - [x] ERROR : 다른 색상일 경우 + - [x] 시작과 도착 위치의 기물이 다른 색상인지 확인 + - [x] ERROR : 같은 색상일 경우 + - [] source 위치에서 target 위치로 기물 이동 + - [] ERROR : target 위치에 같은 색상의 기물이 있을 경우 - [x] 기물(Piece) - [x] 색상 @@ -56,3 +70,10 @@ - [] 킹 - 모든 방향 1칸 이동 (상대의 공격 범위로는 이동 불가능) + +## Advanced + +체스판(Board) + +- [] 위치가 체스판 범위 내인지 확인 + - [] ERROR : 범위 밖일 경우 diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index 0c0d25b..f9a587b 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -2,6 +2,7 @@ import chess.controller.dto.BoardDto; import chess.domain.ChessGame; +import chess.domain.command.Command; import chess.view.InputView; import chess.view.OutputView; @@ -16,10 +17,11 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - String initialCommand = inputView.getInitialCommand(); - ChessGame chessGame = new ChessGame(initialCommand); + ChessGame chessGame = new ChessGame(); while (chessGame.isRunning()) { + Command command = new Command(inputView.getCommand()); + chessGame.run(command); BoardDto boardDto = new BoardDto(chessGame.getBoard()); outputView.printBoard(boardDto); } diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java index d426698..d1462a4 100644 --- a/src/main/java/chess/domain/ChessGame.java +++ b/src/main/java/chess/domain/ChessGame.java @@ -1,28 +1,38 @@ package chess.domain; import chess.domain.board.Board; +import chess.domain.command.Command; +import chess.domain.command.MoveParameters; public class ChessGame { - private static final String START = "start"; - private static final String END = "end"; - private final Board board = new Board(); - private boolean isRunning = false; + private boolean isRunning = true; + private boolean isWhiteTurn = true; + + public ChessGame() { + } - public ChessGame(final String initialCommand) { - if (initialCommand.equals(START)) { - isRunning = true; + public void run(final Command command) { + if (command.isStart()) { return; } - if (initialCommand.equals(END)) { + if (command.isEnd()) { + isRunning = false; return; } + if (command.isMove()) { + move(command.getMoveParameters()); + } + throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); } + private void move(final MoveParameters moveParameters) { + board.move(moveParameters, isWhiteTurn); + } public boolean isRunning() { return isRunning; diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index d099359..ed9e999 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -1,5 +1,6 @@ package chess.domain.board; +import chess.domain.command.MoveParameters; import chess.domain.piece.Bishop; import chess.domain.piece.Color; import chess.domain.piece.King; @@ -10,7 +11,6 @@ import chess.domain.piece.Rook; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -42,19 +42,37 @@ private void initializePawns() { }); } - public boolean isEmpty(Position position) { - return board.get(position) == null; + public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) { + Position source = moveParameters.getSource(); + Position target = moveParameters.getTarget(); + Piece sourcePiece = findBy(source); + + validateColor(isWhiteTurn, sourcePiece.isWhite()); + + Piece targetPiece = findBy(target); + if (sourcePiece.hasSameColor(targetPiece)) { + throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); + } + + board.put(target, sourcePiece); + board.remove(source); } - public Piece findBy(final Position position) { - return board.get(position); + private void validateColor(final boolean expectedColor, final boolean sourcePieceColor) { + if (sourcePieceColor != expectedColor) { + throw new IllegalArgumentException("같은 색상의 기물만 움직일 수 있습니다."); + } } - public Collection getAllPresentPieces() { - return board.values(); + public Piece findBy(final Position position) { + if (isEmpty(position)) { + throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); + } + + return board.get(position); } - public Collection getAllPresentPositions() { - return board.keySet(); + public boolean isEmpty(Position position) { + return board.get(position) == null; } } diff --git a/src/main/java/chess/domain/command/Command.java b/src/main/java/chess/domain/command/Command.java new file mode 100644 index 0000000..ddea82a --- /dev/null +++ b/src/main/java/chess/domain/command/Command.java @@ -0,0 +1,52 @@ +package chess.domain.command; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Command { + + private static final String DELIMITER = " "; + private static final int COMMAND_INDEX = 0; + + private static final String START = "start"; + private static final String END = "end"; + private static final String MOVE = "move"; + + private final String command; + private final List parameters; + + public Command(final String commandLine) { + List chunks = Arrays.stream(commandLine.split(DELIMITER)) + .map(String::trim) + .map(String::toLowerCase) + .collect(Collectors.toList()); + + if (chunks.isEmpty()) { + throw new IllegalArgumentException(); + } + + this.command = chunks.get(COMMAND_INDEX); + this.parameters = chunks.subList(COMMAND_INDEX + 1, chunks.size()); + } + + public boolean isStart() { + return command.equals(START); + } + + public boolean isEnd() { + return command.equals(END); + } + + public boolean isMove() { + return command.equals(MOVE); + } + + public MoveParameters getMoveParameters() { + if (parameters.size() != 2) { + throw new IllegalArgumentException(); + } + + return new MoveParameters(parameters); + } +} diff --git a/src/main/java/chess/domain/command/MoveParameters.java b/src/main/java/chess/domain/command/MoveParameters.java new file mode 100644 index 0000000..44e0947 --- /dev/null +++ b/src/main/java/chess/domain/command/MoveParameters.java @@ -0,0 +1,31 @@ +package chess.domain.command; + +import chess.domain.board.Position; + +import java.util.List; + +public class MoveParameters { + + private static final int SOURCE_INDEX = 0; + private static final int TARGET_INDEX = 1; + + private final Position source; + private final Position target; + + public MoveParameters(final List parameters) { + this(Position.from(parameters.get(SOURCE_INDEX)), Position.from(parameters.get(TARGET_INDEX))); + } + + public MoveParameters(final Position source, final Position target) { + this.source = source; + this.target = target; + } + + public Position getSource() { + return source; + } + + public Position getTarget() { + return target; + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index e7f862b..c704e19 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -12,4 +12,8 @@ public abstract class Piece { public boolean isWhite() { return WHITE == color; } + + public boolean hasSameColor(final Piece target) { + return this.color == target.color; + } } diff --git a/src/main/java/chess/view/ConsoleInputView.java b/src/main/java/chess/view/ConsoleInputView.java index 1438cf8..2f63d25 100644 --- a/src/main/java/chess/view/ConsoleInputView.java +++ b/src/main/java/chess/view/ConsoleInputView.java @@ -7,7 +7,7 @@ public class ConsoleInputView implements InputView { private static final String HEADER = "> "; @Override - public String getInitialCommand() { + public String getCommand() { System.out.println(HEADER + "체스 게임을 시작합니다."); System.out.println(HEADER + "게임 시작은 start, 종료는 end 명령을 입력하세요."); String input = scanner.nextLine(); diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java index 8b5bfc9..3504758 100644 --- a/src/main/java/chess/view/InputView.java +++ b/src/main/java/chess/view/InputView.java @@ -1,5 +1,5 @@ package chess.view; public interface InputView { - String getInitialCommand(); + String getCommand(); } diff --git a/src/test/java/chess/domain/ChessGameTest.java b/src/test/java/chess/domain/ChessGameTest.java index 891110c..54a43d9 100644 --- a/src/test/java/chess/domain/ChessGameTest.java +++ b/src/test/java/chess/domain/ChessGameTest.java @@ -1,5 +1,6 @@ package chess.domain; +import chess.domain.command.Command; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,12 +11,13 @@ public class ChessGameTest { @Test @DisplayName("게임 시작 명령어를 입력받을 경우 게임을 시작한다.") - void create_start_command() { + void run_start_command() { //given - String command = "start"; + ChessGame chessGame = new ChessGame(); + Command command = new Command("start"); //when - ChessGame chessGame = new ChessGame(command); + chessGame.run(command); //then assertThat(chessGame.isRunning()).isTrue(); @@ -23,12 +25,13 @@ void create_start_command() { @Test @DisplayName("게임 종료 명령어를 입력받을 경우 게임을 종료한다.") - void create_end_command() { + void run_end_command() { //given - String command = "end"; + ChessGame chessGame = new ChessGame(); + Command command = new Command("end"); //when - ChessGame chessGame = new ChessGame(command); + chessGame.run(command); //then assertThat(chessGame.isRunning()).isFalse(); @@ -36,11 +39,12 @@ void create_end_command() { @Test @DisplayName("유효하지 않은 명령어를 입력받을 경우 예외를 던진다.") - void create_invalid_command() { + void run_invalid_command() { //given - String command = "invalid_command"; + ChessGame chessGame = new ChessGame(); + Command command = new Command("invalid_command"); //when, then - assertThrows(UnsupportedOperationException.class, () -> new ChessGame(command)); + assertThrows(UnsupportedOperationException.class, () -> chessGame.run(command)); } } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 2cbcee9..8e06054 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,9 +1,12 @@ package chess.domain.board; +import chess.domain.command.MoveParameters; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; public class BoardTest { @@ -21,4 +24,43 @@ void create(String key, boolean expected) { //then assertThat(board.isEmpty(position)).isEqualTo(expected); } + + @Test + @DisplayName("인자로 받은 시작 위치에 기물이 존재하지 않을 경우 예외가 발생한다.") + void move_invalid_source_position() { + //given + Board board = new Board(); + Position source = Position.from("b3"); + Position target = Position.from("b4"); + MoveParameters moveParameters = new MoveParameters(source, target); + + //when, then + assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, true)); + } + + @Test + @DisplayName("인자로 받은 색상과 일치하지 않을 경우 예외가 발생한다.") + void move_different_color() { + //given + Board board = new Board(); + Position source = Position.from("b2"); + Position target = Position.from("b3"); + MoveParameters moveParameters = new MoveParameters(source, target); + + //when, then + assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, false)); + } + + @Test + @DisplayName("인자로 받은 시작과 도착 위치의 기물이 같은 색상일 경우 예외가 발생한다.") + void move_source_and_destination_same_color() { + //given + Board board = new Board(); + Position source = Position.from("a1"); + Position target = Position.from("a2"); + MoveParameters moveParameters = new MoveParameters(source, target); + + //when, then + assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, true)); + } } From 22d29d1f454a5e2930c2ce21db3dcd249c099563 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 20:42:06 +0900 Subject: [PATCH 08/42] =?UTF-8?q?feat:=20=EB=B9=84=EC=88=8D=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- src/main/java/chess/domain/board/Board.java | 7 +++ src/main/java/chess/domain/board/File.java | 21 +++++++ .../java/chess/domain/board/Position.java | 24 +++++++- src/main/java/chess/domain/board/Rank.java | 19 ++++++ .../chess/domain/command/MoveParameters.java | 2 +- src/main/java/chess/domain/piece/Bishop.java | 34 +++++++++++ .../java/chess/domain/piece/Direction.java | 53 +++++++++++++++++ src/main/java/chess/domain/piece/King.java | 9 +++ src/main/java/chess/domain/piece/Knight.java | 9 +++ src/main/java/chess/domain/piece/Pawn.java | 9 +++ src/main/java/chess/domain/piece/Piece.java | 6 ++ src/main/java/chess/domain/piece/Queen.java | 9 +++ src/main/java/chess/domain/piece/Rook.java | 9 +++ .../java/chess/domain/board/BoardTest.java | 14 ++--- .../java/chess/domain/board/PositionTest.java | 2 +- .../java/chess/domain/piece/BishopTest.java | 59 +++++++++++++++++++ .../chess/domain/piece/DirectionTest.java | 21 +++++++ 18 files changed, 298 insertions(+), 12 deletions(-) create mode 100644 src/main/java/chess/domain/piece/Direction.java create mode 100644 src/test/java/chess/domain/piece/BishopTest.java create mode 100644 src/test/java/chess/domain/piece/DirectionTest.java diff --git a/README.md b/README.md index 1e86af3..7bbc323 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ - 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 (진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.) - [] 비숍 (3) - - 모든 대각선 방향으로 원하는 만큼 이동 가능 + - [x] 모든 대각선 방향으로 원하는 만큼 이동 가능 + - [x] ERROR : 비숍 이동 패턴으로 이동할 수 없는 위치일 경우 - [] 퀸 (9) - 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능) diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index ed9e999..b825f4c 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class Board { private final Map board = new HashMap<>(); @@ -45,6 +46,11 @@ private void initializePawns() { public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) { Position source = moveParameters.getSource(); Position target = moveParameters.getTarget(); + + if (source.equals(target)) { + throw new IllegalArgumentException("출발 위치와 도착 위치가 같을 수 없습니다."); + } + Piece sourcePiece = findBy(source); validateColor(isWhiteTurn, sourcePiece.isWhite()); @@ -54,6 +60,7 @@ public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); } + Set passingPositions = sourcePiece.findPaths(source, target); board.put(target, sourcePiece); board.remove(source); } diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java index c30eed7..2cb247b 100644 --- a/src/main/java/chess/domain/board/File.java +++ b/src/main/java/chess/domain/board/File.java @@ -1,5 +1,7 @@ package chess.domain.board; +import java.util.Arrays; + public enum File { a(1), b(2), @@ -16,7 +18,26 @@ public enum File { this.index = index; } + public static File of(final int fileIndex) { + return Arrays.stream(File.values()) + .filter(file -> hasSameIndex(fileIndex, file)) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + + private static boolean hasSameIndex(final int fileIndex, final File file) { + return file.getIndex() == fileIndex; + } + public int getIndex() { return index; } + + public int calculateGap(final File file) { + return this.index - file.index; + } + + public File add(final int amount) { + return File.of(this.index + amount); + } } diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index 8a2be56..b2f50dc 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -1,5 +1,7 @@ package chess.domain.board; +import chess.domain.piece.Direction; + import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -20,12 +22,12 @@ private static String createKey(final File file, final Rank rank) { return file.name() + rank.getIndex(); } - public static Position from(final String key) { + public static Position of(final String key) { return POSITIONS.get(key); } public static Position from(final File file, final Rank rank) { - return POSITIONS.get(createKey(file, rank)); + return of(createKey(file, rank)); } private final File file; @@ -39,4 +41,22 @@ private Position(final File file, final Rank rank) { public File getFile() { return file; } + + public Rank getRank() { + return rank; + } + + public int calculateFileGap(final Position source) { + return file.calculateGap(source.getFile()); + } + + public int calculateRankGap(final Position source) { + return rank.calculateGap(source.getRank()); + } + + public Position move(final Direction direction) { + File file = this.file.add(direction.getFile()); + Rank rank = this.rank.add(direction.getRank()); + return Position.from(file, rank); + } } diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java index 781d032..c76d31c 100644 --- a/src/main/java/chess/domain/board/Rank.java +++ b/src/main/java/chess/domain/board/Rank.java @@ -1,5 +1,7 @@ package chess.domain.board; +import java.util.Arrays; + public enum Rank { R8(8), R7(7), @@ -16,10 +18,27 @@ public enum Rank { this.index = index; } + public static Rank of(final int rankIndex) { + return Arrays.stream(Rank.values()) + .filter(rank -> hasSameIndex(rankIndex, rank)) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + + private static boolean hasSameIndex(final int rankIndex, final Rank rank) { + return rank.index == rankIndex; + } + public int getIndex() { return index; } + public int calculateGap(final Rank rank) { + return this.index - rank.index; + } + public Rank add(final int amount) { + return Rank.of(this.index + amount); + } } diff --git a/src/main/java/chess/domain/command/MoveParameters.java b/src/main/java/chess/domain/command/MoveParameters.java index 44e0947..198b266 100644 --- a/src/main/java/chess/domain/command/MoveParameters.java +++ b/src/main/java/chess/domain/command/MoveParameters.java @@ -13,7 +13,7 @@ public class MoveParameters { private final Position target; public MoveParameters(final List parameters) { - this(Position.from(parameters.get(SOURCE_INDEX)), Position.from(parameters.get(TARGET_INDEX))); + this(Position.of(parameters.get(SOURCE_INDEX)), Position.of(parameters.get(TARGET_INDEX))); } public MoveParameters(final Position source, final Position target) { diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index f18d082..17ed760 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,8 +1,42 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.HashSet; +import java.util.Set; + public class Bishop extends Piece { public Bishop(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + validatePattern(fileGap, rankGap); + + Direction direction = Direction.of(fileGap, rankGap); + return collectPositions(source, target, direction); + } + + private Set collectPositions(final Position source, final Position target, final Direction direction) { + Set positions = new HashSet<>(); + Position current = source; + + while (!target.equals(current)) { + current = current.move(direction); + positions.add(current); + } + + positions.remove(target); + return positions; + } + + private void validatePattern(final int fileGap, final int rankGap) { + if (Math.abs(fileGap) != Math.abs(rankGap)) { + throw new IllegalArgumentException("비숍이 이동할 수 없는 위치입니다."); + } + } } diff --git a/src/main/java/chess/domain/piece/Direction.java b/src/main/java/chess/domain/piece/Direction.java new file mode 100644 index 0000000..b42d0fb --- /dev/null +++ b/src/main/java/chess/domain/piece/Direction.java @@ -0,0 +1,53 @@ +package chess.domain.piece; + +public enum Direction { + NORTH_EAST(1, 1), + SOUTH_EAST(1, -1), + NORTH_WEST(-1, 1), + SOUTH_WEST(-1, -1), + NORTH(0, 1), + SOUTH(0, -1), + EAST(1, 0), + WEST(-1, 0); + + private final int file; + private final int rank; + + Direction(final int file, final int rank) { + this.file = file; + this.rank = rank; + } + + public static Direction of(final int fileGap, final int rankGap) { + if (fileGap > 0 && rankGap > 0) { + return NORTH_EAST; + } + if (fileGap > 0 && rankGap < 0) { + return SOUTH_EAST; + } + if (fileGap < 0 && rankGap > 0) { + return NORTH_WEST; + } + if (fileGap < 0 && rankGap < 0) { + return SOUTH_WEST; + } + if (fileGap > 0) { + return EAST; + } + if (fileGap < 0) { + return WEST; + } + if (rankGap > 0) { + return NORTH; + } + return SOUTH; + } + + public int getFile() { + return file; + } + + public int getRank() { + return rank; + } +} diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index 957ae20..013b5b7 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -1,8 +1,17 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + public class King extends Piece { public King(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + return null; + } } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index 1536a29..f541a52 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,8 +1,17 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + public class Knight extends Piece { public Knight(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + return null; + } } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index 4f3b3fb..2b373a3 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,8 +1,17 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + public class Pawn extends Piece { public Pawn(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + return null; + } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index c704e19..f5178ce 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,5 +1,9 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + import static chess.domain.piece.Color.WHITE; public abstract class Piece { @@ -16,4 +20,6 @@ public boolean isWhite() { public boolean hasSameColor(final Piece target) { return this.color == target.color; } + + public abstract Set findPaths(final Position source, final Position target); } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index a331082..dda7cfc 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -1,8 +1,17 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + public class Queen extends Piece { public Queen(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + return null; + } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index d9eb8a6..9691c13 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,8 +1,17 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Set; + public class Rook extends Piece { public Rook(final Color color) { super(color); } + + @Override + public Set findPaths(final Position source, final Position target) { + return null; + } } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 8e06054..e41d19d 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -16,7 +16,7 @@ public class BoardTest { @DisplayName("객체를 생성한다.") void create(String key, boolean expected) { //given - Position position = Position.from(key); + Position position = Position.of(key); //when Board board = new Board(); @@ -30,8 +30,8 @@ void create(String key, boolean expected) { void move_invalid_source_position() { //given Board board = new Board(); - Position source = Position.from("b3"); - Position target = Position.from("b4"); + Position source = Position.of("b3"); + Position target = Position.of("b4"); MoveParameters moveParameters = new MoveParameters(source, target); //when, then @@ -43,8 +43,8 @@ void move_invalid_source_position() { void move_different_color() { //given Board board = new Board(); - Position source = Position.from("b2"); - Position target = Position.from("b3"); + Position source = Position.of("b2"); + Position target = Position.of("b3"); MoveParameters moveParameters = new MoveParameters(source, target); //when, then @@ -56,8 +56,8 @@ void move_different_color() { void move_source_and_destination_same_color() { //given Board board = new Board(); - Position source = Position.from("a1"); - Position target = Position.from("a2"); + Position source = Position.of("a1"); + Position target = Position.of("a2"); MoveParameters moveParameters = new MoveParameters(source, target); //when, then diff --git a/src/test/java/chess/domain/board/PositionTest.java b/src/test/java/chess/domain/board/PositionTest.java index ad1ef0a..e30fada 100644 --- a/src/test/java/chess/domain/board/PositionTest.java +++ b/src/test/java/chess/domain/board/PositionTest.java @@ -31,7 +31,7 @@ void from_key() { String key = file.name() + rank.getIndex(); //when - Position position = Position.from(key); + Position position = Position.of(key); //then assertThat(position).extracting("file", "rank") diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java new file mode 100644 index 0000000..e9acbdb --- /dev/null +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -0,0 +1,59 @@ +package chess.domain.piece; + +import chess.domain.board.File; +import chess.domain.board.Position; +import chess.domain.board.Rank; +import org.assertj.core.groups.Tuple; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.groups.Tuple.tuple; + +class BishopTest { + + @ParameterizedTest + @MethodSource("createParameters") + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void get_passing_positions_success(String targetPosition, Tuple expected) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new Bishop(Color.WHITE); + + //when + Set passingPositions = piece.findPaths(source, target); + + //then + assertThat(passingPositions).extracting("file", "rank") + .containsOnly(expected); + } + + @Test + @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") + void get_passing_positions_invalid_gap() { + //given + Position source = Position.of("c1"); + Position target = Position.of("f5"); + Piece piece = new Bishop(Color.WHITE); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } + + private static Stream createParameters() { + return Stream.of( + Arguments.of("b6", tuple(File.c, Rank.R5)), + Arguments.of("b2", tuple(File.c, Rank.R3)), + Arguments.of("f2", tuple(File.e, Rank.R3)), + Arguments.of("f6", tuple(File.e, Rank.R5)) + ); + } +} diff --git a/src/test/java/chess/domain/piece/DirectionTest.java b/src/test/java/chess/domain/piece/DirectionTest.java new file mode 100644 index 0000000..14f355a --- /dev/null +++ b/src/test/java/chess/domain/piece/DirectionTest.java @@ -0,0 +1,21 @@ +package chess.domain.piece; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class DirectionTest { + + @ParameterizedTest + @CsvSource({"1,0,EAST", "-1,0,WEST", "0,1,NORTH", "0,-1,SOUTH", "1,1,NORTH_EAST", "1,-1,SOUTH_EAST", "-1,1,NORTH_WEST", "-1,-1,SOUTH_WEST"}) + @DisplayName("방향을 찾는다.") + void find_direction(int fileGap, int rankGap, Direction expected) { + //given, when + Direction direction = Direction.of(fileGap, rankGap); + + //then + assertThat(direction).isEqualTo(expected); + } +} From c877ccd87c7112c5be967d833bed6472a0355391 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 21:04:37 +0900 Subject: [PATCH 09/42] =?UTF-8?q?feat:=20=EB=A3=A9=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- src/main/java/chess/domain/piece/Bishop.java | 31 +--------- src/main/java/chess/domain/piece/King.java | 8 +-- src/main/java/chess/domain/piece/Knight.java | 8 +-- src/main/java/chess/domain/piece/Pawn.java | 8 +-- src/main/java/chess/domain/piece/Piece.java | 33 ++++++++++- src/main/java/chess/domain/piece/Queen.java | 8 +-- src/main/java/chess/domain/piece/Rook.java | 10 ++-- .../java/chess/domain/piece/BishopTest.java | 8 +-- .../java/chess/domain/piece/RookTest.java | 59 +++++++++++++++++++ 10 files changed, 112 insertions(+), 66 deletions(-) create mode 100644 src/test/java/chess/domain/piece/RookTest.java diff --git a/README.md b/README.md index 7bbc323..3353ecc 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,9 @@ - 적 방향 직선 1칸 이동 (처음 이동 시에는 2칸 이동 가능) - 공격 : 적 방향 좌, 우 대각선 1칸 -- [] 룩 (5) - - 모든 직선 방향으로 원하는 만큼 이동 가능 +- [x] 룩 (5) + - [x] 모든 직선 방향으로 원하는 만큼 이동 가능 + - [x] ERROR : 룩 이동 패턴으로 이동할 수 없는 위치일 경우 - [] 나이트 (2.5) - 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 (진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.) diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 17ed760..46ab8f3 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,10 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.HashSet; -import java.util.Set; - public class Bishop extends Piece { public Bishop(final Color color) { @@ -12,30 +7,8 @@ public Bishop(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - int fileGap = target.calculateFileGap(source); - int rankGap = target.calculateRankGap(source); - validatePattern(fileGap, rankGap); - - Direction direction = Direction.of(fileGap, rankGap); - return collectPositions(source, target, direction); - } - - private Set collectPositions(final Position source, final Position target, final Direction direction) { - Set positions = new HashSet<>(); - Position current = source; - - while (!target.equals(current)) { - current = current.move(direction); - positions.add(current); - } - - positions.remove(target); - return positions; - } - - private void validatePattern(final int fileGap, final int rankGap) { - if (Math.abs(fileGap) != Math.abs(rankGap)) { + protected void validatePattern(final int fileGap, final int rankGap) { + if (!isDiagonal(fileGap, rankGap)) { throw new IllegalArgumentException("비숍이 이동할 수 없는 위치입니다."); } } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index 013b5b7..cbe91fd 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -1,9 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Set; - public class King extends Piece { public King(final Color color) { @@ -11,7 +7,7 @@ public King(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - return null; + protected void validatePattern(final int fileGap, final int rankGap) { + } } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index f541a52..7482eed 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,9 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Set; - public class Knight extends Piece { public Knight(final Color color) { @@ -11,7 +7,7 @@ public Knight(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - return null; + protected void validatePattern(final int fileGap, final int rankGap) { + } } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index 2b373a3..d26db1f 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,9 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Set; - public class Pawn extends Piece { public Pawn(final Color color) { @@ -11,7 +7,7 @@ public Pawn(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - return null; + protected void validatePattern(final int fileGap, final int rankGap) { + } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index f5178ce..43ca054 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -2,6 +2,7 @@ import chess.domain.board.Position; +import java.util.HashSet; import java.util.Set; import static chess.domain.piece.Color.WHITE; @@ -13,6 +14,8 @@ public abstract class Piece { this.color = color; } + protected abstract void validatePattern(final int fileGap, final int rankGap); + public boolean isWhite() { return WHITE == color; } @@ -21,5 +24,33 @@ public boolean hasSameColor(final Piece target) { return this.color == target.color; } - public abstract Set findPaths(final Position source, final Position target); + public Set findPaths(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + validatePattern(fileGap, rankGap); + + Direction direction = Direction.of(fileGap, rankGap); + return collectPositions(source, target, direction); + } + + protected boolean isStraight(final int fileGap, final int rankGap) { + return fileGap == 0 || rankGap == 0; + } + + protected boolean isDiagonal(final int fileGap, final int rankGap) { + return Math.abs(fileGap) == Math.abs(rankGap); + } + + public Set collectPositions(final Position source, final Position target, final Direction direction) { + Set positions = new HashSet<>(); + Position current = source; + + while (!target.equals(current)) { + current = current.move(direction); + positions.add(current); + } + + positions.remove(target); + return positions; + } } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index dda7cfc..0ca452f 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -1,9 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Set; - public class Queen extends Piece { public Queen(final Color color) { @@ -11,7 +7,7 @@ public Queen(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - return null; + protected void validatePattern(final int fileGap, final int rankGap) { + } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index 9691c13..31c3e7a 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,9 +1,5 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Set; - public class Rook extends Piece { public Rook(final Color color) { @@ -11,7 +7,9 @@ public Rook(final Color color) { } @Override - public Set findPaths(final Position source, final Position target) { - return null; + protected void validatePattern(final int fileGap, final int rankGap) { + if (!isStraight(fileGap, rankGap)) { + throw new IllegalArgumentException("룩이 이동할 수 없는 위치입니다."); + } } } diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index e9acbdb..2cd3c0d 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -22,23 +22,23 @@ class BishopTest { @ParameterizedTest @MethodSource("createParameters") @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") - void get_passing_positions_success(String targetPosition, Tuple expected) { + void find_paths_success(String targetPosition, Tuple expected) { //given Position source = Position.of("d4"); Position target = Position.of(targetPosition); Piece piece = new Bishop(Color.WHITE); //when - Set passingPositions = piece.findPaths(source, target); + Set paths = piece.findPaths(source, target); //then - assertThat(passingPositions).extracting("file", "rank") + assertThat(paths).extracting("file", "rank") .containsOnly(expected); } @Test @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") - void get_passing_positions_invalid_gap() { + void find_paths_invalid_target() { //given Position source = Position.of("c1"); Position target = Position.of("f5"); diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java new file mode 100644 index 0000000..2e564e3 --- /dev/null +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -0,0 +1,59 @@ +package chess.domain.piece; + +import chess.domain.board.File; +import chess.domain.board.Position; +import chess.domain.board.Rank; +import org.assertj.core.groups.Tuple; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.groups.Tuple.tuple; + +public class RookTest { + + @ParameterizedTest + @MethodSource("createParameters") + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void find_paths_success(String targetPosition, Tuple expected) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new Rook(Color.WHITE); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).extracting("file", "rank") + .containsOnly(expected); + } + + @Test + @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") + void find_paths_invalid_target() { + //given + Position source = Position.of("c1"); + Position target = Position.of("f5"); + Piece piece = new Rook(Color.WHITE); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } + + private static Stream createParameters() { + return Stream.of( + Arguments.of("d2", tuple(File.d, Rank.R3)), + Arguments.of("d6", tuple(File.d, Rank.R5)), + Arguments.of("b4", tuple(File.c, Rank.R4)), + Arguments.of("f4", tuple(File.e, Rank.R4)) + ); + } +} From adc18b63f9204b159f4696850c5a8d3d3b7becb0 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 21:24:19 +0900 Subject: [PATCH 10/42] =?UTF-8?q?feat:=20=ED=80=B8=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- src/main/java/chess/domain/piece/Piece.java | 7 +- src/main/java/chess/domain/piece/Queen.java | 4 +- .../java/chess/domain/piece/QueenTest.java | 76 +++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/test/java/chess/domain/piece/QueenTest.java diff --git a/README.md b/README.md index 3353ecc..ba3a871 100644 --- a/README.md +++ b/README.md @@ -63,12 +63,13 @@ - [] 나이트 (2.5) - 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 (진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.) -- [] 비숍 (3) +- [x] 비숍 (3) - [x] 모든 대각선 방향으로 원하는 만큼 이동 가능 - [x] ERROR : 비숍 이동 패턴으로 이동할 수 없는 위치일 경우 -- [] 퀸 (9) - - 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능) +- [x] 퀸 (9) + - [x] 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능) + - [x] ERROR : 퀸 이동 패턴으로 이동할 수 없는 위치일 경우 - [] 킹 - 모든 방향 1칸 이동 (상대의 공격 범위로는 이동 불가능) diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 43ca054..b45f853 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -6,6 +6,7 @@ import java.util.Set; import static chess.domain.piece.Color.WHITE; +import static java.lang.Math.abs; public abstract class Piece { private final Color color; @@ -37,8 +38,12 @@ protected boolean isStraight(final int fileGap, final int rankGap) { return fileGap == 0 || rankGap == 0; } + protected boolean isFiniteStraight(final int fileGap, final int rankGap) { + return (abs(fileGap) + abs(rankGap)) == 1; + } + protected boolean isDiagonal(final int fileGap, final int rankGap) { - return Math.abs(fileGap) == Math.abs(rankGap); + return abs(fileGap) == abs(rankGap); } public Set collectPositions(final Position source, final Position target, final Direction direction) { diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 0ca452f..8980158 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -8,6 +8,8 @@ public Queen(final Color color) { @Override protected void validatePattern(final int fileGap, final int rankGap) { - + if (!isDiagonal(fileGap, rankGap) && !isFiniteStraight(fileGap, rankGap)) { + throw new IllegalArgumentException("퀸이 이동할 수 없는 위치입니다."); + } } } diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java new file mode 100644 index 0000000..f5d51ab --- /dev/null +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -0,0 +1,76 @@ +package chess.domain.piece; + +import chess.domain.board.File; +import chess.domain.board.Position; +import chess.domain.board.Rank; +import org.assertj.core.groups.Tuple; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.groups.Tuple.tuple; + +class QueenTest { + + @ParameterizedTest + @MethodSource("createParametersForDiagonal") + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void find_paths_success_diagonal(String targetPosition, Tuple expected) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new Queen(Color.WHITE); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).extracting("file", "rank") + .containsOnly(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"d5", "c4", "e4", "d3"}) + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void find_paths_success_straight(String targetPosition) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new Queen(Color.WHITE); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"d6", "d2", "b4", "f4", "f5"}) + @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") + void find_paths_invalid_target(String invalidTarget) { + //given + Position source = Position.of("d4"); + Position target = Position.of(invalidTarget); + Piece piece = new Queen(Color.WHITE); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } + + private static Stream createParametersForDiagonal() { + return Stream.of( + Arguments.of("b6", tuple(File.c, Rank.R5)), + Arguments.of("b2", tuple(File.c, Rank.R3)), + Arguments.of("f2", tuple(File.e, Rank.R3)), + Arguments.of("f6", tuple(File.e, Rank.R5)) + ); + } +} From f3c877a28fc48bd3c0119fdc6122ad30b7705457 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 21:33:37 +0900 Subject: [PATCH 11/42] =?UTF-8?q?feat:=20=ED=82=B9=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++- src/main/java/chess/domain/piece/King.java | 4 +- src/main/java/chess/domain/piece/Piece.java | 10 +++-- .../java/chess/domain/piece/KingTest.java | 43 +++++++++++++++++++ 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/test/java/chess/domain/piece/KingTest.java diff --git a/README.md b/README.md index ba3a871..8f93cba 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,10 @@ - [x] 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능) - [x] ERROR : 퀸 이동 패턴으로 이동할 수 없는 위치일 경우 -- [] 킹 - - 모든 방향 1칸 이동 (상대의 공격 범위로는 이동 불가능) +- [x] 킹 + - [x] 모든 방향 1칸 이동 + - [x] ERROR : 킹 이동 패턴으로 이동할 수 없는 위치일 경우 + - [ ] 상대의 공격 범위로는 이동 불가능 ## Advanced diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index cbe91fd..e5c8d08 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -8,6 +8,8 @@ public King(final Color color) { @Override protected void validatePattern(final int fileGap, final int rankGap) { - + if (!isFiniteStraight(fileGap, rankGap) && !isFiniteDiagonal(fileGap, rankGap)) { + throw new IllegalArgumentException("킹이 이동할 수 없는 위치입니다."); + } } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index b45f853..26ad653 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -28,7 +28,7 @@ public boolean hasSameColor(final Piece target) { public Set findPaths(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); - validatePattern(fileGap, rankGap); + validatePattern(abs(fileGap), abs(rankGap)); Direction direction = Direction.of(fileGap, rankGap); return collectPositions(source, target, direction); @@ -39,11 +39,15 @@ protected boolean isStraight(final int fileGap, final int rankGap) { } protected boolean isFiniteStraight(final int fileGap, final int rankGap) { - return (abs(fileGap) + abs(rankGap)) == 1; + return (fileGap + rankGap) == 1; } protected boolean isDiagonal(final int fileGap, final int rankGap) { - return abs(fileGap) == abs(rankGap); + return fileGap == rankGap; + } + + protected boolean isFiniteDiagonal(final int fileGap, final int rankGap) { + return (fileGap == 1) && (rankGap == 1); } public Set collectPositions(final Position source, final Position target, final Direction direction) { diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java new file mode 100644 index 0000000..abc6459 --- /dev/null +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -0,0 +1,43 @@ +package chess.domain.piece; + +import chess.domain.board.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class KingTest { + + @ParameterizedTest + @ValueSource(strings = {"c3", "c4", "c5", "e3", "e4", "e5", "d3", "d5"}) + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void find_paths_success_straight(String targetPosition) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new King(Color.WHITE); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"c2", "d2", "e2", "f2", "b3", "b4", "b5"}) + @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") + void find_paths_invalid_target(String invalidTarget) { + //given + Position source = Position.of("d4"); + Position target = Position.of(invalidTarget); + Piece piece = new King(Color.WHITE); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } +} From 12da4ca377bc27ec3af1ecad283841ed06ebc05b Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 17 Aug 2021 21:46:27 +0900 Subject: [PATCH 12/42] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++- src/main/java/chess/domain/piece/Knight.java | 21 +++++++++ .../java/chess/domain/piece/KnightTest.java | 43 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/test/java/chess/domain/piece/KnightTest.java diff --git a/README.md b/README.md index 8f93cba..dccfb2d 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,10 @@ - [x] 모든 직선 방향으로 원하는 만큼 이동 가능 - [x] ERROR : 룩 이동 패턴으로 이동할 수 없는 위치일 경우 -- [] 나이트 (2.5) - - 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 (진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.) +- [x] 나이트 (2.5) + - [x] 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능 + - [x] ERROR : 나이트 이동 패턴으로 이동할 수 없는 위치일 경우 + - [x] 진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다. - [x] 비숍 (3) - [x] 모든 대각선 방향으로 원하는 만큼 이동 가능 diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index 7482eed..ad801f4 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,13 +1,34 @@ package chess.domain.piece; +import chess.domain.board.Position; + +import java.util.Collections; +import java.util.Set; + +import static java.lang.Math.abs; + public class Knight extends Piece { public Knight(final Color color) { super(color); } + @Override + public Set findPaths(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + validatePattern(abs(fileGap), abs(rankGap)); + return Collections.emptySet(); + } + @Override protected void validatePattern(final int fileGap, final int rankGap) { + if (!isKnightPattern(fileGap, rankGap)) { + throw new IllegalArgumentException("나이트가 이동할 수 없는 위치입니다."); + } + } + private boolean isKnightPattern(final int fileGap, final int rankGap) { + return (fileGap == 2 && rankGap == 1) || (fileGap == 1 && rankGap == 2); } } diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java new file mode 100644 index 0000000..6f8f198 --- /dev/null +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -0,0 +1,43 @@ +package chess.domain.piece; + +import chess.domain.board.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class KnightTest { + + @ParameterizedTest + @ValueSource(strings = {"c6", "e6", "c2", "e2", "f5", "f3", "b5", "b3"}) + @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") + void find_paths_success_diagonal(String targetPosition) { + //given + Position source = Position.of("d4"); + Position target = Position.of(targetPosition); + Piece piece = new Knight(Color.WHITE); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"c3", "c4", "c5", "e3", "e4", "e5", "d3", "d5"}) + @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") + void find_paths_invalid_target(String invalidTarget) { + //given + Position source = Position.of("d4"); + Position target = Position.of(invalidTarget); + Piece piece = new Knight(Color.WHITE); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } +} From 44ecf395731fb8ddb88607a3e06340c2b85c7d5c Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 19 Aug 2021 14:58:41 +0900 Subject: [PATCH 13/42] =?UTF-8?q?feat:=20=ED=8F=B0=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++- .../java/chess/domain/board/Position.java | 4 + src/main/java/chess/domain/piece/Bishop.java | 2 +- .../java/chess/domain/piece/Direction.java | 40 +++---- src/main/java/chess/domain/piece/King.java | 2 +- src/main/java/chess/domain/piece/Knight.java | 3 +- src/main/java/chess/domain/piece/Pawn.java | 77 ++++++++++++- src/main/java/chess/domain/piece/Piece.java | 7 +- src/main/java/chess/domain/piece/Queen.java | 2 +- src/main/java/chess/domain/piece/Rook.java | 2 +- .../java/chess/domain/piece/KingTest.java | 2 +- .../java/chess/domain/piece/KnightTest.java | 2 +- .../java/chess/domain/piece/PawnTest.java | 104 ++++++++++++++++++ 13 files changed, 226 insertions(+), 34 deletions(-) create mode 100644 src/test/java/chess/domain/piece/PawnTest.java diff --git a/README.md b/README.md index dccfb2d..17a5216 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,16 @@ ## 이동 규칙 -- [] 폰 (1 or 0.5) - - 적 방향 직선 1칸 이동 (처음 이동 시에는 2칸 이동 가능) - - 공격 : 적 방향 좌, 우 대각선 1칸 +- [x] 폰 (1 or 0.5) + - [x] 적 방향 직선 1칸 이동 + - [x] ERROR : 직선 방향 2칸 이상일 경우 + - [x] ERROR : 좌, 우 이동이 포함될 경우 + - [x] 처음 이동 시에는 2칸 이동 가능 + - [x] ERROR : 직선 방향 3칸 이상일 경우 + - [x] ERROR : 좌, 우 이동이 포함될 경우 + + - [x] 공격 : 적 방향 좌, 우 대각선 1칸 + - [x] ERROR : 직선 1칸 && 좌 또는 우 1칸이 아닐 경우 - [x] 룩 (5) - [x] 모든 직선 방향으로 원하는 만큼 이동 가능 diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index b2f50dc..f2acc7e 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -59,4 +59,8 @@ public Position move(final Direction direction) { Rank rank = this.rank.add(direction.getRank()); return Position.from(file, rank); } + + public boolean hasSameRank(final Rank rank) { + return this.rank == rank; + } } diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 46ab8f3..94c96c8 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -7,7 +7,7 @@ public Bishop(final Color color) { } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + protected void validatePattern(int fileGap, int rankGap) { if (!isDiagonal(fileGap, rankGap)) { throw new IllegalArgumentException("비숍이 이동할 수 없는 위치입니다."); } diff --git a/src/main/java/chess/domain/piece/Direction.java b/src/main/java/chess/domain/piece/Direction.java index b42d0fb..5ea3d1a 100644 --- a/src/main/java/chess/domain/piece/Direction.java +++ b/src/main/java/chess/domain/piece/Direction.java @@ -1,5 +1,7 @@ package chess.domain.piece; +import java.util.Arrays; + public enum Direction { NORTH_EAST(1, 1), SOUTH_EAST(1, -1), @@ -10,6 +12,8 @@ public enum Direction { EAST(1, 0), WEST(-1, 0); + private static final int ZERO = 0; + private final int file; private final int rank; @@ -19,28 +23,20 @@ public enum Direction { } public static Direction of(final int fileGap, final int rankGap) { - if (fileGap > 0 && rankGap > 0) { - return NORTH_EAST; - } - if (fileGap > 0 && rankGap < 0) { - return SOUTH_EAST; - } - if (fileGap < 0 && rankGap > 0) { - return NORTH_WEST; - } - if (fileGap < 0 && rankGap < 0) { - return SOUTH_WEST; - } - if (fileGap > 0) { - return EAST; - } - if (fileGap < 0) { - return WEST; - } - if (rankGap > 0) { - return NORTH; - } - return SOUTH; + int fileSign = Integer.compare(fileGap, ZERO); + int rankSign = Integer.compare(rankGap, ZERO); + return findByFileAndRankSign(fileSign, rankSign); + } + + private static Direction findByFileAndRankSign(final int fileSign, final int rankSign) { + return Arrays.stream(Direction.values()) + .filter(direction -> direction.hasSameSigns(fileSign, rankSign)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("매칭되는 방향이 없습니다.")); + } + + private boolean hasSameSigns(final int fileSign, final int rankSign) { + return this.file == fileSign && this.rank == rankSign; } public int getFile() { diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index e5c8d08..89ffd0b 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -7,7 +7,7 @@ public King(final Color color) { } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + protected void validatePattern(int fileGap, int rankGap) { if (!isFiniteStraight(fileGap, rankGap) && !isFiniteDiagonal(fileGap, rankGap)) { throw new IllegalArgumentException("킹이 이동할 수 없는 위치입니다."); } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index ad801f4..c94bae7 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -18,11 +18,12 @@ public Set findPaths(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); validatePattern(abs(fileGap), abs(rankGap)); + return Collections.emptySet(); } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + protected void validatePattern(int fileGap, int rankGap) { if (!isKnightPattern(fileGap, rankGap)) { throw new IllegalArgumentException("나이트가 이동할 수 없는 위치입니다."); } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index d26db1f..cf6ed66 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,13 +1,88 @@ package chess.domain.piece; +import chess.domain.board.Position; +import chess.domain.board.Rank; + +import java.util.Set; + +import static java.lang.Math.abs; + public class Pawn extends Piece { + private static final int VALID_GAP_ON_ATTACK = 1; + private static final int MINIMUM_MOVE_COUNT = 1; + private static final int MAXIMUM_MOVE_COUNT = 2; + private static final Rank WHITE_INITIAL_RANK = Rank.of(2); + private static final Rank BLACK_INITIAL_RANK = Rank.of(7); + + private boolean isInitialMove = true; + public Pawn(final Color color) { super(color); } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + public Set findPaths(final Position source, final Position target) { + setInitialMoveFlag(source); + + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + validatePattern(fileGap, rankGap); + + Direction direction = Direction.of(fileGap, rankGap); + return collectPositions(source, target, direction); + } + + private void setInitialMoveFlag(final Position source) { + if (isWhite() && !source.hasSameRank(WHITE_INITIAL_RANK)) { + isInitialMove = false; + } + + if (isBlack() && !source.hasSameRank(BLACK_INITIAL_RANK)) { + isInitialMove = false; + } + } + + @Override + protected void validatePattern(int fileGap, int rankGap) { + validateForward(rankGap); + + fileGap = abs(fileGap); + rankGap = abs(rankGap); + validateMoveCount(rankGap, fileGap); + } + + private void validateForward(final int rankGap) { + if (isWhite() && rankGap < MINIMUM_MOVE_COUNT) { + throw new IllegalArgumentException(); + } + + if (isBlack() && rankGap > MINIMUM_MOVE_COUNT * -1) { + throw new IllegalArgumentException(); + } + } + + private void validateMoveCount(final int rankGap, final int fileGap) { + if (fileGap != 0) { + validateAttackMoveCount(rankGap, fileGap); + } + + validateForwardMoveCount(rankGap); + } + + private void validateAttackMoveCount(final int rankGap, final int fileGap) { + if (rankGap > VALID_GAP_ON_ATTACK || fileGap > VALID_GAP_ON_ATTACK) { + throw new IllegalArgumentException(); + } + } + + private void validateForwardMoveCount(final int rankGap) { + if (isInitialMove && rankGap > MAXIMUM_MOVE_COUNT) { + throw new IllegalArgumentException(); + } + if (!isInitialMove && rankGap > MINIMUM_MOVE_COUNT) { + throw new IllegalArgumentException(); + } } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 26ad653..17848d2 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.Set; +import static chess.domain.piece.Color.BLACK; import static chess.domain.piece.Color.WHITE; import static java.lang.Math.abs; @@ -18,7 +19,11 @@ public abstract class Piece { protected abstract void validatePattern(final int fileGap, final int rankGap); public boolean isWhite() { - return WHITE == color; + return color == WHITE; + } + + public boolean isBlack() { + return color == BLACK; } public boolean hasSameColor(final Piece target) { diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 8980158..1219815 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -7,7 +7,7 @@ public Queen(final Color color) { } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + protected void validatePattern(int fileGap, int rankGap) { if (!isDiagonal(fileGap, rankGap) && !isFiniteStraight(fileGap, rankGap)) { throw new IllegalArgumentException("퀸이 이동할 수 없는 위치입니다."); } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index 31c3e7a..a09cf42 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -7,7 +7,7 @@ public Rook(final Color color) { } @Override - protected void validatePattern(final int fileGap, final int rankGap) { + protected void validatePattern(int fileGap, int rankGap) { if (!isStraight(fileGap, rankGap)) { throw new IllegalArgumentException("룩이 이동할 수 없는 위치입니다."); } diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java index abc6459..30c8686 100644 --- a/src/test/java/chess/domain/piece/KingTest.java +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -15,7 +15,7 @@ class KingTest { @ParameterizedTest @ValueSource(strings = {"c3", "c4", "c5", "e3", "e4", "e5", "d3", "d5"}) @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") - void find_paths_success_straight(String targetPosition) { + void find_paths_success(String targetPosition) { //given Position source = Position.of("d4"); Position target = Position.of(targetPosition); diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java index 6f8f198..89a7b3f 100644 --- a/src/test/java/chess/domain/piece/KnightTest.java +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -15,7 +15,7 @@ class KnightTest { @ParameterizedTest @ValueSource(strings = {"c6", "e6", "c2", "e2", "f5", "f3", "b5", "b3"}) @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") - void find_paths_success_diagonal(String targetPosition) { + void find_paths_success(String targetPosition) { //given Position source = Position.of("d4"); Position target = Position.of(targetPosition); diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java new file mode 100644 index 0000000..ba8a55d --- /dev/null +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -0,0 +1,104 @@ +package chess.domain.piece; + +import chess.domain.board.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class PawnTest { + + @ParameterizedTest + @CsvSource({"b2, b3, WHITE", "b2, a3, WHITE", "b2, c3, WHITE", + "b7, b6, BLACK", "b7, a6, BLACK", "b7, c6, BLACK"}) + @DisplayName("최초 이동 시 1칸 전진한다.") + void find_paths_success_move_count_one_on_initial_move(String sourcePosition, String targetPosition, Color color) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).isEmpty(); + } + + @ParameterizedTest + @CsvSource({"b2, b4, WHITE, b3", "b7, b5, BLACK, b6"}) + @DisplayName("최초 이동시 2칸 전진하면 지나가는 경로를 반환한다.") + void find_paths_success_move_count_two_on_initial_move(String sourcePosition, String targetPosition, Color color, String expected) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).containsOnly(Position.of(expected)); + } + + @ParameterizedTest + @CsvSource({"d4, d5, WHITE", "d4, c5, WHITE", "d4, e5, WHITE", + "d4, d3, BLACK", "d4, c3, BLACK", "d4, e3, BLACK"}) + @DisplayName("최초 이동이 아닌 경우 1칸 전진한다.") + void find_paths_success_move_count_one(String sourcePosition, String targetPosition, Color color) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when + Set paths = piece.findPaths(source, target); + + //then + assertThat(paths).isEmpty(); + } + + @ParameterizedTest + @CsvSource({"d2, d5, WHITE", "d7, d4, BLACK"}) + @DisplayName("최초 이동 시 2칸 초과 전진하면 예외가 발생한다.") + void find_paths_fail_move_invalid_count_on_initial_move(String sourcePosition, String targetPosition, Color color) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } + + @ParameterizedTest + @CsvSource({"d4, d6, WHITE", "d4, c6, WHITE", "d4, e6, WHITE", + "d4, d2, BLACK", "d4, c2, BLACK", "d4, e2, BLACK"}) + @DisplayName("최초 이동이 아닌 경우 1칸 초과 전진하면 예외가 발생한다.") + void find_paths_fail_move_invalid_count(String sourcePosition, String targetPosition, Color color) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } + + @ParameterizedTest + @CsvSource({"d2, d1, WHITE", "d7, d8, BLACK"}) + @DisplayName("후진 시 예외가 발생한다.") + void find_paths_fail_move_backward(String sourcePosition, String targetPosition, Color color) { + //given + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Piece piece = new Pawn(color); + + //when //then + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + } +} From 4498862ec143de1caf6910f665840f0691853ddf Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 19 Aug 2021 15:40:08 +0900 Subject: [PATCH 14/42] =?UTF-8?q?feat:=20=EC=B2=B4=EC=8A=A4=ED=8C=90?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=94=BC=EC=8A=A4=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++-- src/main/java/chess/domain/board/Board.java | 50 ++++++++++---- .../java/chess/domain/board/BoardTest.java | 68 +++++++++++++++---- 3 files changed, 95 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 17a5216..bde670b 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,12 @@ - [x] ERROR : 다른 색상일 경우 - [x] 시작과 도착 위치의 기물이 다른 색상인지 확인 - [x] ERROR : 같은 색상일 경우 - - [] source 위치에서 target 위치로 기물 이동 - - [] ERROR : target 위치에 같은 색상의 기물이 있을 경우 + - [x] source 위치에서 target 위치로 기물 이동 + - [x] ERROR : source 위치에 기물이 없는 경우 + - [x] ERROR : 자신의 기물이 아닌 경우 + - [x] ERROR : source, target 위치의 기물 색상이 같을 경우 + - [x] ERROR : source, target 위치가 같을 경우 + - [x] ERROR : 이동 경로에 기물이 존재할 경우 - [x] 기물(Piece) - [x] 색상 @@ -85,9 +89,3 @@ - [x] ERROR : 킹 이동 패턴으로 이동할 수 없는 위치일 경우 - [ ] 상대의 공격 범위로는 이동 불가능 -## Advanced - -체스판(Board) - -- [] 위치가 체스판 범위 내인지 확인 - - [] ERROR : 범위 밖일 경우 diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index b825f4c..d2cb3e9 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -46,28 +46,27 @@ private void initializePawns() { public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) { Position source = moveParameters.getSource(); Position target = moveParameters.getTarget(); - - if (source.equals(target)) { - throw new IllegalArgumentException("출발 위치와 도착 위치가 같을 수 없습니다."); - } - Piece sourcePiece = findBy(source); - validateColor(isWhiteTurn, sourcePiece.isWhite()); + validateOwner(isWhiteTurn, sourcePiece.isWhite()); + validateSamePosition(source, target); + validateTarget(target, sourcePiece); - Piece targetPiece = findBy(target); - if (sourcePiece.hasSameColor(targetPiece)) { - throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); - } + Set paths = sourcePiece.findPaths(source, target); + validatePathsEmpty(paths); - Set passingPositions = sourcePiece.findPaths(source, target); board.put(target, sourcePiece); board.remove(source); } - private void validateColor(final boolean expectedColor, final boolean sourcePieceColor) { - if (sourcePieceColor != expectedColor) { - throw new IllegalArgumentException("같은 색상의 기물만 움직일 수 있습니다."); + private void validateTarget(final Position target, final Piece sourcePiece) { + if (isEmpty(target)) { + return; + } + + Piece targetPiece = board.get(target); + if (sourcePiece.hasSameColor(targetPiece)) { + throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); } } @@ -80,6 +79,27 @@ public Piece findBy(final Position position) { } public boolean isEmpty(Position position) { - return board.get(position) == null; + return !board.containsKey(position); + } + + private void validateOwner(final boolean expectedColor, final boolean sourcePieceColor) { + if (sourcePieceColor != expectedColor) { + throw new IllegalArgumentException("자신의 기물만 움직일 수 있습니다."); + } + } + + private void validateSamePosition(final Position source, final Position target) { + if (source.equals(target)) { + throw new IllegalArgumentException("출발 위치와 도착 위치가 같을 수 없습니다."); + } + } + + private void validatePathsEmpty(final Set paths) { + boolean isPresent = paths.stream() + .anyMatch(board::containsKey); + + if (isPresent) { + throw new IllegalArgumentException("기물을 통과하여 이동할 수 없습니다."); + } } } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index e41d19d..00af2a3 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -27,7 +27,7 @@ void create(String key, boolean expected) { @Test @DisplayName("인자로 받은 시작 위치에 기물이 존재하지 않을 경우 예외가 발생한다.") - void move_invalid_source_position() { + void move_source_position_empty() { //given Board board = new Board(); Position source = Position.of("b3"); @@ -35,32 +35,72 @@ void move_invalid_source_position() { MoveParameters moveParameters = new MoveParameters(source, target); //when, then - assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, true)); + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, true)) + .withMessage("해당 위치에 기물이 존재하지 않습니다."); } - @Test - @DisplayName("인자로 받은 색상과 일치하지 않을 경우 예외가 발생한다.") - void move_different_color() { + @ParameterizedTest + @CsvSource({"b2, b3, false", "a7, a6, true"}) + @DisplayName("자신의 기물이 아닌 기물을 선택할 경우 예외가 발생한다.") + void move_source_not_owner(String sourcePosition, String targetPosition, boolean isWhiteTurn) { //given Board board = new Board(); - Position source = Position.of("b2"); - Position target = Position.of("b3"); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); MoveParameters moveParameters = new MoveParameters(source, target); //when, then - assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, false)); + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .withMessage("자신의 기물만 움직일 수 있습니다."); } - @Test - @DisplayName("인자로 받은 시작과 도착 위치의 기물이 같은 색상일 경우 예외가 발생한다.") - void move_source_and_destination_same_color() { + @ParameterizedTest + @CsvSource({"a1, a2, true", "a8, a7, false"}) + @DisplayName("시작과 도착 위치의 기물이 같은 색상일 경우 예외가 발생한다.") + void move_source_and_target_same_color(String sourcePosition, String targetPosition, boolean isWhiteTurn) { + //given + Board board = new Board(); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + MoveParameters moveParameters = new MoveParameters(source, target); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .withMessage("같은 색상의 기물은 공격할 수 없습니다."); + } + + @ParameterizedTest + @CsvSource({"a1, a1, true", "a8, a8, false"}) + @DisplayName("시작과 도착 위치가 같을 경우 예외가 발생한다.") + void move_source_and_target_same(String sourcePosition, String targetPosition, boolean isWhiteTurn) { + //given + Board board = new Board(); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + MoveParameters moveParameters = new MoveParameters(source, target); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .withMessage("출발 위치와 도착 위치가 같을 수 없습니다."); + } + + @ParameterizedTest + @CsvSource({"a1, a3, true", "a8, a6, false"}) + @DisplayName("경로에 다른 기물이 존재하는 경우 예외가 발생한다.") + void move_invalid_paths(String sourcePosition, String targetPosition, boolean isWhiteTurn) { //given Board board = new Board(); - Position source = Position.of("a1"); - Position target = Position.of("a2"); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); MoveParameters moveParameters = new MoveParameters(source, target); //when, then - assertThatIllegalArgumentException().isThrownBy(() -> board.move(moveParameters, true)); + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .withMessage("기물을 통과하여 이동할 수 없습니다."); } } From a40508df22586f4247f5280ddcb4778af889ddac Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 19 Aug 2021 17:24:48 +0900 Subject: [PATCH 15/42] =?UTF-8?q?refactor:=20=ED=94=BC=EC=8A=A4=20?= =?UTF-8?q?=EC=83=89=EC=83=81=EB=B3=84=20=EC=B2=B4=EC=8A=A4=ED=8C=90=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++ src/main/java/chess/domain/board/Board.java | 111 ++++++++---------- .../java/chess/domain/piece/PieceFactory.java | 51 ++++++++ src/main/java/chess/domain/player/Player.java | 42 +++++++ .../java/chess/view/ConsoleOutputView.java | 1 - .../java/chess/domain/board/BoardTest.java | 14 ++- .../java/chess/domain/player/PlayerTest.java | 79 +++++++++++++ 7 files changed, 234 insertions(+), 72 deletions(-) create mode 100644 src/main/java/chess/domain/piece/PieceFactory.java create mode 100644 src/main/java/chess/domain/player/Player.java create mode 100644 src/test/java/chess/domain/player/PlayerTest.java diff --git a/README.md b/README.md index bde670b..c6ee1b7 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,14 @@ - [x] 기물 이름 매퍼 - [x] 기물 종류에 따라 이름을 매핑 +- [ ] 플레이어(Player) + - [x] 자신의 기물 + - [ ] 자신이 공격 가능한 범위 + + - 기능 + - [x] 기물을 이동시킨다. + - [x] 기물의 이동 경로를 반환한다. + - [x] 입력받은 위치에 기물이 있는지 확인한다. ## 이동 규칙 diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index d2cb3e9..ad5ef27 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -1,89 +1,43 @@ package chess.domain.board; import chess.domain.command.MoveParameters; -import chess.domain.piece.Bishop; import chess.domain.piece.Color; -import chess.domain.piece.King; -import chess.domain.piece.Knight; -import chess.domain.piece.Pawn; import chess.domain.piece.Piece; -import chess.domain.piece.Queen; -import chess.domain.piece.Rook; +import chess.domain.player.Player; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.Set; public class Board { - private final Map board = new HashMap<>(); + private final Player white; + private final Player black; public Board() { - initializePawns(); - initializeOthers(Rank.R1, Color.WHITE); - initializeOthers(Rank.R8, Color.BLACK); - } - - private void initializeOthers(Rank rank, Color color) { - board.put(Position.from(File.a, rank), new Rook(color)); - board.put(Position.from(File.b, rank), new Knight(color)); - board.put(Position.from(File.c, rank), new Bishop(color)); - board.put(Position.from(File.d, rank), new Queen(color)); - board.put(Position.from(File.e, rank), new King(color)); - board.put(Position.from(File.f, rank), new Bishop(color)); - board.put(Position.from(File.g, rank), new Knight(color)); - board.put(Position.from(File.h, rank), new Rook(color)); - } - - private void initializePawns() { - Arrays.stream(File.values()) - .forEach(file -> { - board.put(Position.from(file, Rank.R2), new Pawn(Color.WHITE)); - board.put(Position.from(file, Rank.R7), new Pawn(Color.BLACK)); - }); + this.white = new Player(Color.WHITE); + this.black = new Player(Color.BLACK); } public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) { + Player player = currentPlayer(isWhiteTurn); + Player enemy = currentPlayer(!isWhiteTurn); Position source = moveParameters.getSource(); Position target = moveParameters.getTarget(); - Piece sourcePiece = findBy(source); - validateOwner(isWhiteTurn, sourcePiece.isWhite()); + validateSourceOwner(enemy, source); validateSamePosition(source, target); - validateTarget(target, sourcePiece); + validateTarget(player, target); - Set paths = sourcePiece.findPaths(source, target); - validatePathsEmpty(paths); - - board.put(target, sourcePiece); - board.remove(source); + movePiece(player, source, target); } - private void validateTarget(final Position target, final Piece sourcePiece) { - if (isEmpty(target)) { - return; - } - - Piece targetPiece = board.get(target); - if (sourcePiece.hasSameColor(targetPiece)) { - throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); + private Player currentPlayer(final boolean isWhiteTurn) { + if (isWhiteTurn) { + return white; } + return black; } - public Piece findBy(final Position position) { - if (isEmpty(position)) { - throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); - } - - return board.get(position); - } - - public boolean isEmpty(Position position) { - return !board.containsKey(position); - } - - private void validateOwner(final boolean expectedColor, final boolean sourcePieceColor) { - if (sourcePieceColor != expectedColor) { + private void validateSourceOwner(final Player enemy, final Position source) { + if (enemy.hasPieceOn(source)) { throw new IllegalArgumentException("자신의 기물만 움직일 수 있습니다."); } } @@ -94,12 +48,39 @@ private void validateSamePosition(final Position source, final Position target) } } + private void validateTarget(final Player player, final Position target) { + if (player.hasPieceOn(target)) { + throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다."); + } + } + + private void movePiece(final Player player, final Position source, final Position target) { + Set paths = player.findPaths(source, target); + validatePathsEmpty(paths); + + player.update(source, target); + } + private void validatePathsEmpty(final Set paths) { - boolean isPresent = paths.stream() - .anyMatch(board::containsKey); + boolean isWhiteBlocked = paths.stream() + .anyMatch(white::hasPieceOn); + boolean isBlackBlocked = paths.stream() + .anyMatch(black::hasPieceOn); - if (isPresent) { + if (isWhiteBlocked || isBlackBlocked) { throw new IllegalArgumentException("기물을 통과하여 이동할 수 없습니다."); } } + + public Piece findBy(final Position position) { + if (white.hasPieceOn(position)) { + return white.findPieceBy(position); + } + + return black.findPieceBy(position); + } + + public boolean isEmpty(Position position) { + return !white.hasPieceOn(position) && !black.hasPieceOn(position); + } } diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java new file mode 100644 index 0000000..b3d23d3 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -0,0 +1,51 @@ +package chess.domain.piece; + +import chess.domain.board.File; +import chess.domain.board.Position; +import chess.domain.board.Rank; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static chess.domain.piece.Color.BLACK; +import static chess.domain.piece.Color.WHITE; + +public class PieceFactory { + + private PieceFactory() { + } + + public static Map createPieces(Color color) { + if (color == WHITE) { + return whitePieces(); + } + return blackPieces(); + } + + private static Map whitePieces() { + return initializePieces(Rank.R1, Rank.R2, WHITE); + } + + private static Map blackPieces() { + return initializePieces(Rank.R8, Rank.R7, BLACK); + } + + private static Map initializePieces(final Rank rank, final Rank pawnRank, final Color color) { + Map board = new HashMap<>(); + + board.put(Position.from(File.a, rank), new Rook(color)); + board.put(Position.from(File.b, rank), new Knight(color)); + board.put(Position.from(File.c, rank), new Bishop(color)); + board.put(Position.from(File.d, rank), new Queen(color)); + board.put(Position.from(File.e, rank), new King(color)); + board.put(Position.from(File.f, rank), new Bishop(color)); + board.put(Position.from(File.g, rank), new Knight(color)); + board.put(Position.from(File.h, rank), new Rook(color)); + + Arrays.stream(File.values()) + .forEach(file -> board.put(Position.from(file, pawnRank), new Pawn(color))); + + return board; + } +} diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java new file mode 100644 index 0000000..a0f247a --- /dev/null +++ b/src/main/java/chess/domain/player/Player.java @@ -0,0 +1,42 @@ +package chess.domain.player; + +import chess.domain.board.Position; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class Player { + + private final Map board; + + public Player(final Color color) { + board = new HashMap<>(PieceFactory.createPieces(color)); + } + + public boolean hasPieceOn(final Position position) { + return board.containsKey(position); + } + + public Set findPaths(final Position source, final Position target) { + Piece sourcePiece = findPieceBy(source); + return sourcePiece.findPaths(source, target); + } + + public void update(final Position source, final Position target) { + Piece sourcePiece = findPieceBy(source); + board.put(target, sourcePiece); + board.remove(source); + } + + public Piece findPieceBy(final Position position) { + if (!hasPieceOn(position)) { + throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); + } + + return board.get(position); + } +} diff --git a/src/main/java/chess/view/ConsoleOutputView.java b/src/main/java/chess/view/ConsoleOutputView.java index 8d08771..3733513 100644 --- a/src/main/java/chess/view/ConsoleOutputView.java +++ b/src/main/java/chess/view/ConsoleOutputView.java @@ -4,7 +4,6 @@ public class ConsoleOutputView implements OutputView { - @Override public void printBoard(final BoardDto boardDto) { boardDto.getPositionDtos() diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 00af2a3..71e6d55 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,28 +1,30 @@ package chess.domain.board; import chess.domain.command.MoveParameters; +import chess.domain.piece.Pawn; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; public class BoardTest { - @ParameterizedTest - @CsvSource({"a1, false", "a3, true"}) + @Test @DisplayName("객체를 생성한다.") - void create(String key, boolean expected) { + void create() { //given - Position position = Position.of(key); + Position pawnPosition = Position.of("b2"); + Position emptyPosition = Position.of("b3"); //when Board board = new Board(); //then - assertThat(board.isEmpty(position)).isEqualTo(expected); + assertThat(board.findBy(pawnPosition)).isInstanceOf(Pawn.class); + assertThat(board.isEmpty(emptyPosition)).isTrue(); } @Test diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java new file mode 100644 index 0000000..260e86c --- /dev/null +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -0,0 +1,79 @@ +package chess.domain.player; + +import chess.domain.board.Position; +import chess.domain.piece.Color; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +public class PlayerTest { + + @ParameterizedTest + @CsvSource({"b2, true", "b3, false"}) + @DisplayName("피스 색상을 넣어서 플레이어 객체를 생성한다.") + void create_with_color(String key, boolean expected) { + //given + Position position = Position.of(key); + Color color = Color.WHITE; + + // when + Player player = new Player(color); + + //then + assertThat(player.hasPieceOn(position)).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource({"WHITE, b3, b4", "BLACK, b6, b5"}) + @DisplayName("시작 위치에 기물이 존재하지 않을 경우 예외가 발생한다.") + void update_source_position_empty(Color color, String sourcePosition, String targetPosition) { + //given + Player player = new Player(color); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> player.update(source, target)) + .withMessage("해당 위치에 기물이 존재하지 않습니다."); + } + + @ParameterizedTest + @CsvSource({"WHITE, b2, b3", "BLACK, d7, d6"}) + @DisplayName("기물을 움직인다.") + void update_board(Color color, String sourcePosition, String targetPosition) { + //given + Player player = new Player(color); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + + //when + player.update(source, target); + + //then + assertThat(player.hasPieceOn(source)).isFalse(); + assertThat(player.hasPieceOn(target)).isTrue(); + } + + @ParameterizedTest + @CsvSource({"WHITE, b2, b4, b3", "BLACK, d7, d5, d6"}) + @DisplayName("이동 경로를 반환한다.") + void find_paths(Color color, String sourcePosition, String targetPosition, String expected) { + //given + Player player = new Player(color); + Position source = Position.of(sourcePosition); + Position target = Position.of(targetPosition); + Position path = Position.of(expected); + + //when + Set paths = player.findPaths(source, target); + + //then + assertThat(paths).containsOnly(path); + } +} From c30f2426777dc58504dd745292ec899dea2d3063 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 19 Aug 2021 19:45:58 +0900 Subject: [PATCH 16/42] =?UTF-8?q?feat:=20=EB=B9=84=EC=88=8D,=20=EB=A3=A9?= =?UTF-8?q?=20=EA=B3=B5=EA=B2=A9=20=EA=B0=80=EB=8A=A5=ED=95=9C=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/ChessGame.java | 1 + src/main/java/chess/domain/board/File.java | 5 ++++ .../java/chess/domain/board/Position.java | 18 +++++++++++++++ src/main/java/chess/domain/board/Rank.java | 5 ++++ src/main/java/chess/domain/piece/Bishop.java | 12 +++++++++- src/main/java/chess/domain/piece/Piece.java | 17 ++++++++++---- src/main/java/chess/domain/piece/Rook.java | 12 +++++++++- .../java/chess/domain/piece/BishopTest.java | 23 +++++++++++++++++++ .../java/chess/domain/piece/RookTest.java | 23 +++++++++++++++++++ 9 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java index d1462a4..305244f 100644 --- a/src/main/java/chess/domain/ChessGame.java +++ b/src/main/java/chess/domain/ChessGame.java @@ -32,6 +32,7 @@ public void run(final Command command) { private void move(final MoveParameters moveParameters) { board.move(moveParameters, isWhiteTurn); + isWhiteTurn = !isWhiteTurn; } public boolean isRunning() { diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java index 2cb247b..8a18a09 100644 --- a/src/main/java/chess/domain/board/File.java +++ b/src/main/java/chess/domain/board/File.java @@ -40,4 +40,9 @@ public int calculateGap(final File file) { public File add(final int amount) { return File.of(this.index + amount); } + + public boolean canMove(final int amount) { + int fileIndex = index + amount; + return fileIndex >= a.index && fileIndex <= h.index; + } } diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index f2acc7e..52ca177 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -3,6 +3,8 @@ import chess.domain.piece.Direction; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; @@ -63,4 +65,20 @@ public Position move(final Direction direction) { public boolean hasSameRank(final Rank rank) { return this.rank == rank; } + + public Collection findAvailablePositions(final Direction direction) { + Collection positions = new HashSet<>(); + + Position current = this; + while (current.isMovable(direction)) { + current = current.move(direction); + positions.add(current); + } + + return positions; + } + + private boolean isMovable(final Direction direction) { + return rank.canMove(direction.getRank()) && file.canMove(direction.getFile()); + } } diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java index c76d31c..549e78e 100644 --- a/src/main/java/chess/domain/board/Rank.java +++ b/src/main/java/chess/domain/board/Rank.java @@ -40,5 +40,10 @@ public int calculateGap(final Rank rank) { public Rank add(final int amount) { return Rank.of(this.index + amount); } + + public boolean canMove(final int amount) { + int rankIndex = index + amount; + return rankIndex >= R1.index && rankIndex <= R8.index; + } } diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 94c96c8..9a7854f 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,9 +1,19 @@ package chess.domain.piece; +import java.util.Arrays; +import java.util.Collection; + +import static chess.domain.piece.Direction.NORTH_EAST; +import static chess.domain.piece.Direction.NORTH_WEST; +import static chess.domain.piece.Direction.SOUTH_EAST; +import static chess.domain.piece.Direction.SOUTH_WEST; + public class Bishop extends Piece { + private static final Collection DIRECTIONS = Arrays.asList(NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST); + public Bishop(final Color color) { - super(color); + super(DIRECTIONS, color); } @Override diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 17848d2..7e21a16 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -2,17 +2,21 @@ import chess.domain.board.Position; +import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; import static chess.domain.piece.Color.BLACK; import static chess.domain.piece.Color.WHITE; import static java.lang.Math.abs; public abstract class Piece { + private final Collection DIRECTIONS; private final Color color; - Piece(final Color color) { + Piece(final Collection DIRECTIONS, final Color color) { + this.DIRECTIONS = DIRECTIONS; this.color = color; } @@ -26,10 +30,6 @@ public boolean isBlack() { return color == BLACK; } - public boolean hasSameColor(final Piece target) { - return this.color == target.color; - } - public Set findPaths(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); @@ -67,4 +67,11 @@ public Set collectPositions(final Position source, final Position targ positions.remove(target); return positions; } + + public Collection findAvailableAttackPositions(Position position) { + return DIRECTIONS.stream() + .map(position::findAvailablePositions) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index a09cf42..e8eecfc 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,9 +1,19 @@ package chess.domain.piece; +import java.util.Arrays; +import java.util.Collection; + +import static chess.domain.piece.Direction.EAST; +import static chess.domain.piece.Direction.NORTH; +import static chess.domain.piece.Direction.SOUTH; +import static chess.domain.piece.Direction.WEST; + public class Rook extends Piece { + private static final Collection DIRECTIONS = Arrays.asList(EAST, WEST, NORTH, SOUTH); + public Rook(final Color color) { - super(color); + super(DIRECTIONS, color); } @Override diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index 2cd3c0d..8213b40 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -10,6 +10,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import java.util.stream.Stream; @@ -48,6 +50,27 @@ void find_paths_invalid_target() { assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); } + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece bishop = new Bishop(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("a1"), Position.of("b2"), Position.of("c3"), Position.of("e5"), + Position.of("a7"), Position.of("b6"), Position.of("c5"), Position.of("e3"), + Position.of("f6"), Position.of("g7"), Position.of("h8"), + Position.of("f2"), Position.of("g1") + ); + + //when + Collection availableAttackPositions = bishop.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .containsAll(expected); + } + private static Stream createParameters() { return Stream.of( Arguments.of("b6", tuple(File.c, Rank.R5)), diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java index 2e564e3..6d17c21 100644 --- a/src/test/java/chess/domain/piece/RookTest.java +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -10,6 +10,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import java.util.stream.Stream; @@ -48,6 +50,27 @@ void find_paths_invalid_target() { assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); } + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece rook = new Rook(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("d1"), Position.of("d2"), Position.of("d3"), Position.of("d5"), + Position.of("d6"), Position.of("d7"), Position.of("d8"), + Position.of("a4"), Position.of("b4"), Position.of("c4"), Position.of("e4"), + Position.of("f4"), Position.of("g4"), Position.of("h4") + ); + + //when + Collection availableAttackPositions = rook.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .containsAll(expected); + } + private static Stream createParameters() { return Stream.of( Arguments.of("d2", tuple(File.d, Rank.R3)), From 483c149337b8319d81dd8e00f92a3aced0f337a2 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 20 Aug 2021 20:48:37 +0900 Subject: [PATCH 17/42] =?UTF-8?q?feat:=20=ED=8F=B0=20=EC=A0=9C=EC=99=B8?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EB=AC=BC=20=ED=8C=A8=ED=84=B4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/board/Position.java | 49 ++++++-- src/main/java/chess/domain/piece/Bishop.java | 19 +-- src/main/java/chess/domain/piece/Color.java | 7 +- .../java/chess/domain/piece/Direction.java | 49 -------- src/main/java/chess/domain/piece/King.java | 11 +- src/main/java/chess/domain/piece/Knight.java | 29 +---- .../chess/domain/piece/MoveCoordinate.java | 72 +++++++++++ src/main/java/chess/domain/piece/Pawn.java | 76 +---------- src/main/java/chess/domain/piece/Piece.java | 60 +++------ src/main/java/chess/domain/piece/Queen.java | 11 +- src/main/java/chess/domain/piece/Rook.java | 19 +-- .../domain/piece/pattern/MovePattern.java | 118 ++++++++++++++++++ .../chess/domain/player/AttackPositions.java | 27 ++++ src/main/java/chess/domain/player/Player.java | 21 ++-- .../java/chess/domain/piece/BishopTest.java | 5 +- .../chess/domain/piece/DirectionTest.java | 21 ---- .../java/chess/domain/piece/KingTest.java | 28 ++++- .../java/chess/domain/piece/KnightTest.java | 27 +++- .../java/chess/domain/piece/PawnTest.java | 34 ++++- .../java/chess/domain/piece/QueenTest.java | 30 ++++- .../java/chess/domain/piece/RookTest.java | 4 +- .../domain/player/AttackPositionsTest.java | 27 ++++ 22 files changed, 447 insertions(+), 297 deletions(-) delete mode 100644 src/main/java/chess/domain/piece/Direction.java create mode 100644 src/main/java/chess/domain/piece/MoveCoordinate.java create mode 100644 src/main/java/chess/domain/piece/pattern/MovePattern.java create mode 100644 src/main/java/chess/domain/player/AttackPositions.java delete mode 100644 src/test/java/chess/domain/piece/DirectionTest.java create mode 100644 src/test/java/chess/domain/player/AttackPositionsTest.java diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index 52ca177..03c8607 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -1,12 +1,14 @@ package chess.domain.board; -import chess.domain.piece.Direction; +import chess.domain.piece.MoveCoordinate; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; public class Position { private static final Map POSITIONS = createPositions(); @@ -56,29 +58,56 @@ public int calculateRankGap(final Position source) { return rank.calculateGap(source.getRank()); } - public Position move(final Direction direction) { - File file = this.file.add(direction.getFile()); - Rank rank = this.rank.add(direction.getRank()); - return Position.from(file, rank); + public Set findPassingPositions(Position target, MoveCoordinate moveCoordinate) { + Set positions = new HashSet<>(); + Position current = this; + + while (!target.equals(current)) { + current = current.move(moveCoordinate); + positions.add(current); + } + + positions.remove(target); + return positions; } public boolean hasSameRank(final Rank rank) { return this.rank == rank; } - public Collection findAvailablePositions(final Direction direction) { + public Collection findAvailablePositions(final MoveCoordinate moveCoordinate, final boolean isFinite) { + if (isFinite) { + return getFinitePositions(moveCoordinate); + } + return getInfinitePositions(moveCoordinate); + } + + private Collection getFinitePositions(MoveCoordinate moveCoordinate) { + if (isMovable(moveCoordinate)) { + return Collections.singleton(move(moveCoordinate)); + } + return Collections.emptySet(); + } + + private Collection getInfinitePositions(MoveCoordinate moveCoordinate) { Collection positions = new HashSet<>(); Position current = this; - while (current.isMovable(direction)) { - current = current.move(direction); + while (current.isMovable(moveCoordinate)) { + current = current.move(moveCoordinate); positions.add(current); } return positions; } - private boolean isMovable(final Direction direction) { - return rank.canMove(direction.getRank()) && file.canMove(direction.getFile()); + private boolean isMovable(final MoveCoordinate moveCoordinate) { + return rank.canMove(moveCoordinate.getRank()) && file.canMove(moveCoordinate.getFile()); + } + + private Position move(final MoveCoordinate moveCoordinate) { + File file = this.file.add(moveCoordinate.getFile()); + Rank rank = this.rank.add(moveCoordinate.getRank()); + return Position.from(file, rank); } } diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 9a7854f..6265e9c 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,25 +1,10 @@ package chess.domain.piece; -import java.util.Arrays; -import java.util.Collection; - -import static chess.domain.piece.Direction.NORTH_EAST; -import static chess.domain.piece.Direction.NORTH_WEST; -import static chess.domain.piece.Direction.SOUTH_EAST; -import static chess.domain.piece.Direction.SOUTH_WEST; +import chess.domain.piece.pattern.MovePattern; public class Bishop extends Piece { - private static final Collection DIRECTIONS = Arrays.asList(NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST); - public Bishop(final Color color) { - super(DIRECTIONS, color); - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - if (!isDiagonal(fileGap, rankGap)) { - throw new IllegalArgumentException("비숍이 이동할 수 없는 위치입니다."); - } + super(MovePattern.bishopPattern(), color); } } diff --git a/src/main/java/chess/domain/piece/Color.java b/src/main/java/chess/domain/piece/Color.java index 517538f..f61d576 100644 --- a/src/main/java/chess/domain/piece/Color.java +++ b/src/main/java/chess/domain/piece/Color.java @@ -2,5 +2,10 @@ public enum Color { WHITE, - BLACK + BLACK; + + public boolean isWhite() { + return this == WHITE; + + } } diff --git a/src/main/java/chess/domain/piece/Direction.java b/src/main/java/chess/domain/piece/Direction.java deleted file mode 100644 index 5ea3d1a..0000000 --- a/src/main/java/chess/domain/piece/Direction.java +++ /dev/null @@ -1,49 +0,0 @@ -package chess.domain.piece; - -import java.util.Arrays; - -public enum Direction { - NORTH_EAST(1, 1), - SOUTH_EAST(1, -1), - NORTH_WEST(-1, 1), - SOUTH_WEST(-1, -1), - NORTH(0, 1), - SOUTH(0, -1), - EAST(1, 0), - WEST(-1, 0); - - private static final int ZERO = 0; - - private final int file; - private final int rank; - - Direction(final int file, final int rank) { - this.file = file; - this.rank = rank; - } - - public static Direction of(final int fileGap, final int rankGap) { - int fileSign = Integer.compare(fileGap, ZERO); - int rankSign = Integer.compare(rankGap, ZERO); - return findByFileAndRankSign(fileSign, rankSign); - } - - private static Direction findByFileAndRankSign(final int fileSign, final int rankSign) { - return Arrays.stream(Direction.values()) - .filter(direction -> direction.hasSameSigns(fileSign, rankSign)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("매칭되는 방향이 없습니다.")); - } - - private boolean hasSameSigns(final int fileSign, final int rankSign) { - return this.file == fileSign && this.rank == rankSign; - } - - public int getFile() { - return file; - } - - public int getRank() { - return rank; - } -} diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index 89ffd0b..855d923 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -1,15 +1,10 @@ package chess.domain.piece; +import chess.domain.piece.pattern.MovePattern; + public class King extends Piece { public King(final Color color) { - super(color); - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - if (!isFiniteStraight(fileGap, rankGap) && !isFiniteDiagonal(fileGap, rankGap)) { - throw new IllegalArgumentException("킹이 이동할 수 없는 위치입니다."); - } + super(MovePattern.kingPattern(), color); } } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index c94bae7..4ed9f31 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,35 +1,10 @@ package chess.domain.piece; -import chess.domain.board.Position; - -import java.util.Collections; -import java.util.Set; - -import static java.lang.Math.abs; +import chess.domain.piece.pattern.MovePattern; public class Knight extends Piece { public Knight(final Color color) { - super(color); - } - - @Override - public Set findPaths(final Position source, final Position target) { - int fileGap = target.calculateFileGap(source); - int rankGap = target.calculateRankGap(source); - validatePattern(abs(fileGap), abs(rankGap)); - - return Collections.emptySet(); - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - if (!isKnightPattern(fileGap, rankGap)) { - throw new IllegalArgumentException("나이트가 이동할 수 없는 위치입니다."); - } - } - - private boolean isKnightPattern(final int fileGap, final int rankGap) { - return (fileGap == 2 && rankGap == 1) || (fileGap == 1 && rankGap == 2); + super(MovePattern.knightPattern(), color); } } diff --git a/src/main/java/chess/domain/piece/MoveCoordinate.java b/src/main/java/chess/domain/piece/MoveCoordinate.java new file mode 100644 index 0000000..ef14d67 --- /dev/null +++ b/src/main/java/chess/domain/piece/MoveCoordinate.java @@ -0,0 +1,72 @@ +package chess.domain.piece; + +public enum MoveCoordinate { + NORTH_EAST(1, 1), + SOUTH_EAST(1, -1), + NORTH_WEST(-1, 1), + SOUTH_WEST(-1, -1), + NORTH(0, 1), + SOUTH(0, -1), + EAST(1, 0), + WEST(-1, 0), + + WHITE_PAWN_INITIAL_NORTH(0, 2), + BLACK_PAWN_INITIAL_SOUTH(0, -2), + + NORTH_EAST_LEFT(1, 2), + NORTH_EAST_RIGHT(2, 1), + NORTH_WEST_LEFT(-2, 1), + NORTH_WEST_RIGHT(-1, 2), + SOUTH_EAST_LEFT(2, -1), + SOUTH_EAST_RIGHT(1, -2), + SOUTH_WEST_LEFT(-1, -2), + SOUTH_WEST_RIGHT(-2, -1); + + private final int file; + private final int rank; + + MoveCoordinate(final int file, final int rank) { + this.file = file; + this.rank = rank; + } + + public boolean matches(final int fileGap, final int rankGap, boolean isFinite) { + if (isFinite) { + return this.file == fileGap && this.rank == rankGap; + } + + if (this.file == 0) { + return this.file == fileGap && (rankGap % this.rank == 0) && hasSameSign(fileGap, rankGap); + } + + if (this.rank == 0) { + return this.rank == rankGap && (fileGap % this.file == 0) && hasSameSign(fileGap, rankGap); + } + + return isMultiple(fileGap, rankGap) && hasSameSign(fileGap, rankGap); + } + + private boolean isMultiple(final int fileGap, final int rankGap) { + return hasSameRate(fileGap, rankGap) && isDivisible(fileGap, rankGap); + } + + private boolean isDivisible(final int fileGap, final int rankGap) { + return (fileGap % this.file == 0) && (rankGap % this.rank == 0); + } + + private boolean hasSameRate(final int fileGap, final int rankGap) { + return (fileGap / this.file) == (rankGap / this.rank); + } + + private boolean hasSameSign(final int fileGap, final int rankGap) { + return (file ^ fileGap) >= 0 && (rank ^ rankGap) >= 0; + } + + public int getFile() { + return file; + } + + public int getRank() { + return rank; + } +} diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index cf6ed66..e65c1ca 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,88 +1,16 @@ package chess.domain.piece; -import chess.domain.board.Position; -import chess.domain.board.Rank; - -import java.util.Set; - -import static java.lang.Math.abs; +import chess.domain.piece.pattern.MovePattern; public class Pawn extends Piece { private static final int VALID_GAP_ON_ATTACK = 1; private static final int MINIMUM_MOVE_COUNT = 1; private static final int MAXIMUM_MOVE_COUNT = 2; - private static final Rank WHITE_INITIAL_RANK = Rank.of(2); - private static final Rank BLACK_INITIAL_RANK = Rank.of(7); private boolean isInitialMove = true; public Pawn(final Color color) { - super(color); - } - - @Override - public Set findPaths(final Position source, final Position target) { - setInitialMoveFlag(source); - - int fileGap = target.calculateFileGap(source); - int rankGap = target.calculateRankGap(source); - validatePattern(fileGap, rankGap); - - Direction direction = Direction.of(fileGap, rankGap); - return collectPositions(source, target, direction); - } - - private void setInitialMoveFlag(final Position source) { - if (isWhite() && !source.hasSameRank(WHITE_INITIAL_RANK)) { - isInitialMove = false; - } - - if (isBlack() && !source.hasSameRank(BLACK_INITIAL_RANK)) { - isInitialMove = false; - } - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - validateForward(rankGap); - - fileGap = abs(fileGap); - rankGap = abs(rankGap); - validateMoveCount(rankGap, fileGap); - } - - private void validateForward(final int rankGap) { - if (isWhite() && rankGap < MINIMUM_MOVE_COUNT) { - throw new IllegalArgumentException(); - } - - if (isBlack() && rankGap > MINIMUM_MOVE_COUNT * -1) { - throw new IllegalArgumentException(); - } - } - - private void validateMoveCount(final int rankGap, final int fileGap) { - if (fileGap != 0) { - validateAttackMoveCount(rankGap, fileGap); - } - - validateForwardMoveCount(rankGap); - } - - private void validateAttackMoveCount(final int rankGap, final int fileGap) { - if (rankGap > VALID_GAP_ON_ATTACK || fileGap > VALID_GAP_ON_ATTACK) { - throw new IllegalArgumentException(); - } - } - - private void validateForwardMoveCount(final int rankGap) { - if (isInitialMove && rankGap > MAXIMUM_MOVE_COUNT) { - throw new IllegalArgumentException(); - } - - if (!isInitialMove && rankGap > MINIMUM_MOVE_COUNT) { - throw new IllegalArgumentException(); - } + super(MovePattern.pawnPattern(color), color); } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 7e21a16..26aad3a 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,6 +1,7 @@ package chess.domain.piece; import chess.domain.board.Position; +import chess.domain.piece.pattern.MovePattern; import java.util.Collection; import java.util.HashSet; @@ -9,19 +10,17 @@ import static chess.domain.piece.Color.BLACK; import static chess.domain.piece.Color.WHITE; -import static java.lang.Math.abs; public abstract class Piece { - private final Collection DIRECTIONS; + + protected final MovePattern movePattern; private final Color color; - Piece(final Collection DIRECTIONS, final Color color) { - this.DIRECTIONS = DIRECTIONS; + Piece(final MovePattern movePattern, final Color color) { + this.movePattern = movePattern; this.color = color; } - protected abstract void validatePattern(final int fileGap, final int rankGap); - public boolean isWhite() { return color == WHITE; } @@ -30,48 +29,27 @@ public boolean isBlack() { return color == BLACK; } - public Set findPaths(final Position source, final Position target) { + public Set findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); - validatePattern(abs(fileGap), abs(rankGap)); - - Direction direction = Direction.of(fileGap, rankGap); - return collectPositions(source, target, direction); - } - - protected boolean isStraight(final int fileGap, final int rankGap) { - return fileGap == 0 || rankGap == 0; - } - - protected boolean isFiniteStraight(final int fileGap, final int rankGap) { - return (fileGap + rankGap) == 1; - } - - protected boolean isDiagonal(final int fileGap, final int rankGap) { - return fileGap == rankGap; - } - protected boolean isFiniteDiagonal(final int fileGap, final int rankGap) { - return (fileGap == 1) && (rankGap == 1); + MoveCoordinate moveCoordinate = movePattern.findMoveCoordinate(fileGap, rankGap); + return source.findPassingPositions(target, moveCoordinate); } - public Set collectPositions(final Position source, final Position target, final Direction direction) { - Set positions = new HashSet<>(); - Position current = source; - - while (!target.equals(current)) { - current = current.move(direction); - positions.add(current); - } - - positions.remove(target); - return positions; - } + public Collection findAvailableAttackPositions(final Position position) { + Set finitePositions = movePattern.finiteMoveCoordinates().stream() + .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, true)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); - public Collection findAvailableAttackPositions(Position position) { - return DIRECTIONS.stream() - .map(position::findAvailablePositions) + Set infinitePositions = movePattern.infiniteMoveCoordinates().stream() + .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, false)) .flatMap(Collection::stream) .collect(Collectors.toSet()); + + Collection positions = new HashSet<>(finitePositions); + positions.addAll(infinitePositions); + return positions; } } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 1219815..7389d31 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -1,15 +1,10 @@ package chess.domain.piece; +import chess.domain.piece.pattern.MovePattern; + public class Queen extends Piece { public Queen(final Color color) { - super(color); - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - if (!isDiagonal(fileGap, rankGap) && !isFiniteStraight(fileGap, rankGap)) { - throw new IllegalArgumentException("퀸이 이동할 수 없는 위치입니다."); - } + super(MovePattern.queenPattern(), color); } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index e8eecfc..2e9f2c6 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,25 +1,10 @@ package chess.domain.piece; -import java.util.Arrays; -import java.util.Collection; - -import static chess.domain.piece.Direction.EAST; -import static chess.domain.piece.Direction.NORTH; -import static chess.domain.piece.Direction.SOUTH; -import static chess.domain.piece.Direction.WEST; +import chess.domain.piece.pattern.MovePattern; public class Rook extends Piece { - private static final Collection DIRECTIONS = Arrays.asList(EAST, WEST, NORTH, SOUTH); - public Rook(final Color color) { - super(DIRECTIONS, color); - } - - @Override - protected void validatePattern(int fileGap, int rankGap) { - if (!isStraight(fileGap, rankGap)) { - throw new IllegalArgumentException("룩이 이동할 수 없는 위치입니다."); - } + super(MovePattern.rookPattern(), color); } } diff --git a/src/main/java/chess/domain/piece/pattern/MovePattern.java b/src/main/java/chess/domain/piece/pattern/MovePattern.java new file mode 100644 index 0000000..1fbd46b --- /dev/null +++ b/src/main/java/chess/domain/piece/pattern/MovePattern.java @@ -0,0 +1,118 @@ +package chess.domain.piece.pattern; + +import chess.domain.piece.Color; +import chess.domain.piece.MoveCoordinate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; +import static chess.domain.piece.MoveCoordinate.EAST; +import static chess.domain.piece.MoveCoordinate.NORTH; +import static chess.domain.piece.MoveCoordinate.NORTH_EAST; +import static chess.domain.piece.MoveCoordinate.NORTH_EAST_LEFT; +import static chess.domain.piece.MoveCoordinate.NORTH_EAST_RIGHT; +import static chess.domain.piece.MoveCoordinate.NORTH_WEST; +import static chess.domain.piece.MoveCoordinate.NORTH_WEST_LEFT; +import static chess.domain.piece.MoveCoordinate.NORTH_WEST_RIGHT; +import static chess.domain.piece.MoveCoordinate.SOUTH; +import static chess.domain.piece.MoveCoordinate.SOUTH_EAST; +import static chess.domain.piece.MoveCoordinate.SOUTH_EAST_LEFT; +import static chess.domain.piece.MoveCoordinate.SOUTH_EAST_RIGHT; +import static chess.domain.piece.MoveCoordinate.SOUTH_WEST; +import static chess.domain.piece.MoveCoordinate.SOUTH_WEST_LEFT; +import static chess.domain.piece.MoveCoordinate.SOUTH_WEST_RIGHT; +import static chess.domain.piece.MoveCoordinate.WEST; +import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; + +public class MovePattern { + + private static final Collection CARDINAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( + NORTH, SOUTH, WEST, EAST + )); + + private static final Collection DIAGONAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( + NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST + )); + + private static final Collection WHITE_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( + WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH + )); + + private static final Collection BLACK_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( + BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH + )); + + private static final Collection KNIGHT_COORDINATES = Collections.unmodifiableList(Arrays.asList( + NORTH_EAST_LEFT, NORTH_EAST_RIGHT, NORTH_WEST_LEFT, NORTH_WEST_RIGHT, + SOUTH_EAST_LEFT, SOUTH_EAST_RIGHT, SOUTH_WEST_LEFT, SOUTH_WEST_RIGHT + )); + + private final Collection infiniteMoveCoordinates; + private final Collection finiteMoveCoordinates; + + private MovePattern(final Collection infiniteMoveCoordinates, final Collection finiteMoveCoordinates) { + this.infiniteMoveCoordinates = Collections.unmodifiableCollection(infiniteMoveCoordinates); + this.finiteMoveCoordinates = Collections.unmodifiableCollection(finiteMoveCoordinates); + } + + public static MovePattern queenPattern() { + return new MovePattern(DIAGONAL_COORDINATES, CARDINAL_COORDINATES); + } + + public static MovePattern kingPattern() { + List finiteMoveCoordinates = new ArrayList<>(CARDINAL_COORDINATES); + finiteMoveCoordinates.addAll(DIAGONAL_COORDINATES); + return new MovePattern(Collections.emptyList(), finiteMoveCoordinates); + } + + public static MovePattern knightPattern() { + return new MovePattern(Collections.emptyList(), KNIGHT_COORDINATES); + } + + public static MovePattern rookPattern() { + return new MovePattern(CARDINAL_COORDINATES, Collections.emptyList()); + } + + public static MovePattern bishopPattern() { + return new MovePattern(DIAGONAL_COORDINATES, Collections.emptyList()); + } + + public static MovePattern pawnPattern(final Color color) { + if (color.isWhite()) { + new MovePattern(Collections.emptyList(), WHITE_PAWN_COORDINATES); + } + return new MovePattern(Collections.emptyList(), BLACK_PAWN_COORDINATES); + } + + public MoveCoordinate findMoveCoordinate(int fileGap, int rankGap) { + List result = new ArrayList<>(); + + infiniteMoveCoordinates.stream() + .filter(moveCoordinate -> moveCoordinate.matches(fileGap, rankGap, false)) + .findAny() + .ifPresent(result::add); + + finiteMoveCoordinates.stream() + .filter(moveCoordinate -> moveCoordinate.matches(fileGap, rankGap, true)) + .findAny() + .ifPresent(result::add); + + if (result.isEmpty()) { + throw new IllegalArgumentException("매칭되는 방향이 없습니다."); + } + + return result.get(0); + } + + public Collection finiteMoveCoordinates() { + return finiteMoveCoordinates; + } + + public Collection infiniteMoveCoordinates() { + return infiniteMoveCoordinates; + } +} diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackPositions.java new file mode 100644 index 0000000..ff817d8 --- /dev/null +++ b/src/main/java/chess/domain/player/AttackPositions.java @@ -0,0 +1,27 @@ +package chess.domain.player; + +import chess.domain.board.Position; +import chess.domain.piece.Piece; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class AttackPositions { + + private final Map counts = new HashMap<>(); + + public AttackPositions(final Map pieces) { + pieces.keySet() + .forEach(position -> { + Piece piece = pieces.get(position); + Collection positions = piece.findAvailableAttackPositions(position); + positions.forEach(target + -> counts.put(target, counts.getOrDefault(target, 0) + 1)); + }); + } + + public void update(final Position source, final Position target) { +// Collection positions = source.findAvailablePositions(); + } +} diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index a0f247a..1e041df 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -11,25 +11,32 @@ public class Player { - private final Map board; + private final Map pieces; + private final AttackPositions attackPositions; public Player(final Color color) { - board = new HashMap<>(PieceFactory.createPieces(color)); + pieces = new HashMap<>(PieceFactory.createPieces(color)); + attackPositions = new AttackPositions(pieces); } public boolean hasPieceOn(final Position position) { - return board.containsKey(position); + return pieces.containsKey(position); } public Set findPaths(final Position source, final Position target) { Piece sourcePiece = findPieceBy(source); - return sourcePiece.findPaths(source, target); + return sourcePiece.findPath(source, target); } public void update(final Position source, final Position target) { + movePiece(source, target); + attackPositions.update(source, target); + } + + private void movePiece(final Position source, final Position target) { Piece sourcePiece = findPieceBy(source); - board.put(target, sourcePiece); - board.remove(source); + pieces.put(target, sourcePiece); + pieces.remove(source); } public Piece findPieceBy(final Position position) { @@ -37,6 +44,6 @@ public Piece findPieceBy(final Position position) { throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); } - return board.get(position); + return pieces.get(position); } } diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index 8213b40..e8080f3 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -31,7 +31,7 @@ void find_paths_success(String targetPosition, Tuple expected) { Piece piece = new Bishop(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") @@ -47,7 +47,7 @@ void find_paths_invalid_target() { Piece piece = new Bishop(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); } @Test @@ -68,6 +68,7 @@ void find_available_attack_positions() { //then assertThat(availableAttackPositions) + .hasSize(expected.size()) .containsAll(expected); } diff --git a/src/test/java/chess/domain/piece/DirectionTest.java b/src/test/java/chess/domain/piece/DirectionTest.java deleted file mode 100644 index 14f355a..0000000 --- a/src/test/java/chess/domain/piece/DirectionTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package chess.domain.piece; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import static org.assertj.core.api.Assertions.assertThat; - -class DirectionTest { - - @ParameterizedTest - @CsvSource({"1,0,EAST", "-1,0,WEST", "0,1,NORTH", "0,-1,SOUTH", "1,1,NORTH_EAST", "1,-1,SOUTH_EAST", "-1,1,NORTH_WEST", "-1,-1,SOUTH_WEST"}) - @DisplayName("방향을 찾는다.") - void find_direction(int fileGap, int rankGap, Direction expected) { - //given, when - Direction direction = Direction.of(fileGap, rankGap); - - //then - assertThat(direction).isEqualTo(expected); - } -} diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java index 30c8686..ce71a38 100644 --- a/src/test/java/chess/domain/piece/KingTest.java +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -2,9 +2,12 @@ import chess.domain.board.Position; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -22,7 +25,7 @@ void find_paths_success(String targetPosition) { Piece piece = new King(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -38,6 +41,27 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new King(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + } + + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece king = new King(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("d3"), Position.of("d5"), + Position.of("c3"), Position.of("c4"), Position.of("c5"), + Position.of("e3"), Position.of("e4"), Position.of("e5") + ); + + //when + Collection availableAttackPositions = king.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .hasSize(expected.size()) + .containsAll(expected); } } diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java index 89a7b3f..3979525 100644 --- a/src/test/java/chess/domain/piece/KnightTest.java +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -2,9 +2,12 @@ import chess.domain.board.Position; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -22,7 +25,7 @@ void find_paths_success(String targetPosition) { Piece piece = new Knight(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -38,6 +41,26 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new Knight(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + } + + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece knight = new Knight(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("c6"), Position.of("c2"), Position.of("e6"), Position.of("e2"), + Position.of("b5"), Position.of("b3"), Position.of("f5"), Position.of("f3") + ); + + //when + Collection availableAttackPositions = knight.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .hasSize(expected.size()) + .containsAll(expected); } } diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java index ba8a55d..16993f1 100644 --- a/src/test/java/chess/domain/piece/PawnTest.java +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -2,9 +2,12 @@ import chess.domain.board.Position; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -23,7 +26,7 @@ void find_paths_success_move_count_one_on_initial_move(String sourcePosition, St Piece piece = new Pawn(color); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -39,7 +42,7 @@ void find_paths_success_move_count_two_on_initial_move(String sourcePosition, St Piece piece = new Pawn(color); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).containsOnly(Position.of(expected)); @@ -56,7 +59,7 @@ void find_paths_success_move_count_one(String sourcePosition, String targetPosit Piece piece = new Pawn(color); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -72,7 +75,7 @@ void find_paths_fail_move_invalid_count_on_initial_move(String sourcePosition, S Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); } @ParameterizedTest @@ -86,7 +89,7 @@ void find_paths_fail_move_invalid_count(String sourcePosition, String targetPosi Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); } @ParameterizedTest @@ -99,6 +102,25 @@ void find_paths_fail_move_backward(String sourcePosition, String targetPosition, Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + } + + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece pawn = new Pawn(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("c5"), Position.of("e5") + ); + + //when + Collection availableAttackPositions = pawn.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .hasSize(expected.size()) + .containsAll(expected); } } diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java index f5d51ab..b1f85e6 100644 --- a/src/test/java/chess/domain/piece/QueenTest.java +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -5,11 +5,14 @@ import chess.domain.board.Rank; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import java.util.Arrays; +import java.util.Collection; import java.util.Set; import java.util.stream.Stream; @@ -29,7 +32,7 @@ void find_paths_success_diagonal(String targetPosition, Tuple expected) { Piece piece = new Queen(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") @@ -46,7 +49,7 @@ void find_paths_success_straight(String targetPosition) { Piece piece = new Queen(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -62,7 +65,28 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new Queen(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + } + + @Test + @DisplayName("입력받은 위치에서 공격 가능한 위치들을 반환해준다.") + void find_available_attack_positions() { + //given + Position position = Position.of("d4"); + Piece queen = new Queen(Color.WHITE); + Collection expected = Arrays.asList( + Position.of("a1"), Position.of("b2"), Position.of("c3"), Position.of("e5"), Position.of("f6"), Position.of("g7"), Position.of("h8"), + Position.of("a7"), Position.of("b6"), Position.of("c5"), Position.of("e3"), Position.of("f2"), Position.of("g1"), + Position.of("d3"), Position.of("d5"), Position.of("c4"), Position.of("e4") + ); + + //when + Collection availableAttackPositions = queen.findAvailableAttackPositions(position); + + //then + assertThat(availableAttackPositions) + .hasSize(expected.size()) + .containsAll(expected); } private static Stream createParametersForDiagonal() { diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java index 6d17c21..a787a58 100644 --- a/src/test/java/chess/domain/piece/RookTest.java +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -31,7 +31,7 @@ void find_paths_success(String targetPosition, Tuple expected) { Piece piece = new Rook(Color.WHITE); //when - Set paths = piece.findPaths(source, target); + Set paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") @@ -47,7 +47,7 @@ void find_paths_invalid_target() { Piece piece = new Rook(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPaths(source, target)); + assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); } @Test diff --git a/src/test/java/chess/domain/player/AttackPositionsTest.java b/src/test/java/chess/domain/player/AttackPositionsTest.java new file mode 100644 index 0000000..ab83b88 --- /dev/null +++ b/src/test/java/chess/domain/player/AttackPositionsTest.java @@ -0,0 +1,27 @@ +package chess.domain.player; + +import chess.domain.board.Position; +import chess.domain.piece.Color; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceFactory; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +class AttackPositionsTest { + + @Test + @DisplayName("기물들이 주어지면 공격 가능한 위치들이 초기화된다.") + void create() { + //given + Map pieces = PieceFactory.createPieces(Color.WHITE); + + //when + AttackPositions attackPositions = new AttackPositions(pieces); + + //then + + } + +} From c2f0deaacb4a0ddff1ceeae18868d129e1302371 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 20 Aug 2021 22:50:16 +0900 Subject: [PATCH 18/42] =?UTF-8?q?feat:=20=EA=B8=B0=EB=AC=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EC=8B=9C=20=EA=B3=B5=EA=B2=A9=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=20=EC=9C=84=EC=B9=98=EB=A5=BC=20=EA=B0=B1=EC=8B=A0=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../java/chess/domain/board/Position.java | 8 +-- .../chess/domain/piece/MoveCoordinate.java | 6 ++ src/main/java/chess/domain/piece/Pawn.java | 69 +++++++++++++++++-- .../domain/piece/mapper/PieceMappers.java | 4 +- .../domain/piece/pattern/MovePattern.java | 17 ++++- .../chess/domain/player/AttackPositions.java | 24 +++++-- src/main/java/chess/domain/player/Player.java | 8 +-- .../domain/piece/pattern/MovePatternTest.java | 59 ++++++++++++++++ .../domain/player/AttackPositionsTest.java | 35 ++++++++-- 10 files changed, 207 insertions(+), 27 deletions(-) create mode 100644 src/test/java/chess/domain/piece/pattern/MovePatternTest.java diff --git a/README.md b/README.md index c6ee1b7..bb1e4a7 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ - [x] 기물 이름 매퍼 - [x] 기물 종류에 따라 이름을 매핑 -- [ ] 플레이어(Player) +- [x] 플레이어(Player) - [x] 자신의 기물 - - [ ] 자신이 공격 가능한 범위 + - [x] 자신이 공격 가능한 범위 - 기능 - [x] 기물을 이동시킨다. diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/board/Position.java index 03c8607..8b32741 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/board/Position.java @@ -71,10 +71,6 @@ public Set findPassingPositions(Position target, MoveCoordinate moveCo return positions; } - public boolean hasSameRank(final Rank rank) { - return this.rank == rank; - } - public Collection findAvailablePositions(final MoveCoordinate moveCoordinate, final boolean isFinite) { if (isFinite) { return getFinitePositions(moveCoordinate); @@ -110,4 +106,8 @@ private Position move(final MoveCoordinate moveCoordinate) { Rank rank = this.rank.add(moveCoordinate.getRank()); return Position.from(file, rank); } + + public boolean hasSameRank(Rank rank) { + return this.rank == rank; + } } diff --git a/src/main/java/chess/domain/piece/MoveCoordinate.java b/src/main/java/chess/domain/piece/MoveCoordinate.java index ef14d67..7cc93d0 100644 --- a/src/main/java/chess/domain/piece/MoveCoordinate.java +++ b/src/main/java/chess/domain/piece/MoveCoordinate.java @@ -1,5 +1,7 @@ package chess.domain.piece; +import static java.lang.Math.abs; + public enum MoveCoordinate { NORTH_EAST(1, 1), SOUTH_EAST(1, -1), @@ -62,6 +64,10 @@ private boolean hasSameSign(final int fileGap, final int rankGap) { return (file ^ fileGap) >= 0 && (rank ^ rankGap) >= 0; } + public int countMove() { + return abs(this.file) + abs(this.rank); + } + public int getFile() { return file; } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index e65c1ca..f72e35c 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,16 +1,75 @@ package chess.domain.piece; +import chess.domain.board.Position; +import chess.domain.board.Rank; import chess.domain.piece.pattern.MovePattern; -public class Pawn extends Piece { +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; +import static chess.domain.piece.MoveCoordinate.NORTH; +import static chess.domain.piece.MoveCoordinate.SOUTH; +import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; - private static final int VALID_GAP_ON_ATTACK = 1; - private static final int MINIMUM_MOVE_COUNT = 1; - private static final int MAXIMUM_MOVE_COUNT = 2; +public class Pawn extends Piece { - private boolean isInitialMove = true; + private static final Rank WHITE_INITIAL_RANK = Rank.R2; + private static final Rank BLACK_INITIAL_RANK = Rank.R7; public Pawn(final Color color) { super(MovePattern.pawnPattern(color), color); } + + @Override + public Set findPath(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + + MoveCoordinate moveCoordinate = movePattern.findMoveCoordinate(fileGap, rankGap); + moveCoordinate = resetIfInitialMove(moveCoordinate, isInitialMove(source)); + + return source.findPassingPositions(target, moveCoordinate); + } + + private boolean isInitialMove(Position source) { + return source.hasSameRank(WHITE_INITIAL_RANK) || source.hasSameRank(BLACK_INITIAL_RANK); + } + + private MoveCoordinate resetIfInitialMove(MoveCoordinate moveCoordinate, boolean isInitialMove) { + validateMoveCount(moveCoordinate, isInitialMove); + + if (isWhiteInitialMove(moveCoordinate)) { + return NORTH; + } + + if (isBlackInitialMove(moveCoordinate)) { + return SOUTH; + } + + return moveCoordinate; + } + + private void validateMoveCount(MoveCoordinate moveCoordinate, boolean isInitialMove) { + if (!isInitialMove && (isBlackInitialMove(moveCoordinate) || isWhiteInitialMove(moveCoordinate))) { + throw new IllegalArgumentException("최초 이동 시에만 2칸 이동할 수 있습니다."); + } + } + + private boolean isWhiteInitialMove(final MoveCoordinate moveCoordinate) { + return moveCoordinate == WHITE_PAWN_INITIAL_NORTH; + } + + private boolean isBlackInitialMove(final MoveCoordinate moveCoordinate) { + return moveCoordinate == BLACK_PAWN_INITIAL_SOUTH; + } + + @Override + public Collection findAvailableAttackPositions(final Position position) { + return movePattern.pawnAttackMoveCoordinates(isWhite()).stream() + .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, true)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/chess/domain/piece/mapper/PieceMappers.java b/src/main/java/chess/domain/piece/mapper/PieceMappers.java index be4d012..cc53e31 100644 --- a/src/main/java/chess/domain/piece/mapper/PieceMappers.java +++ b/src/main/java/chess/domain/piece/mapper/PieceMappers.java @@ -28,9 +28,7 @@ public static String findNameBy(final Piece piece) { .filter(mapper -> mapper.supports(piece)) .map(mapper -> mapper.findNameBy(piece)) .findAny() - .orElseThrow(() -> { - throw new NoSuchElementException("해당 기물에 해당하는 매퍼가 존재하지 않습니다."); - }); + .orElseThrow(() -> new NoSuchElementException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); } private PieceMappers() { diff --git a/src/main/java/chess/domain/piece/pattern/MovePattern.java b/src/main/java/chess/domain/piece/pattern/MovePattern.java index 1fbd46b..9f82c26 100644 --- a/src/main/java/chess/domain/piece/pattern/MovePattern.java +++ b/src/main/java/chess/domain/piece/pattern/MovePattern.java @@ -42,10 +42,18 @@ public class MovePattern { WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH )); + private static final Collection WHITE_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( + NORTH_EAST, NORTH_WEST + )); + private static final Collection BLACK_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH )); + private static final Collection BLACK_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( + SOUTH_EAST, SOUTH_WEST + )); + private static final Collection KNIGHT_COORDINATES = Collections.unmodifiableList(Arrays.asList( NORTH_EAST_LEFT, NORTH_EAST_RIGHT, NORTH_WEST_LEFT, NORTH_WEST_RIGHT, SOUTH_EAST_LEFT, SOUTH_EAST_RIGHT, SOUTH_WEST_LEFT, SOUTH_WEST_RIGHT @@ -83,7 +91,7 @@ public static MovePattern bishopPattern() { public static MovePattern pawnPattern(final Color color) { if (color.isWhite()) { - new MovePattern(Collections.emptyList(), WHITE_PAWN_COORDINATES); + return new MovePattern(Collections.emptyList(), WHITE_PAWN_COORDINATES); } return new MovePattern(Collections.emptyList(), BLACK_PAWN_COORDINATES); } @@ -115,4 +123,11 @@ public Collection finiteMoveCoordinates() { public Collection infiniteMoveCoordinates() { return infiniteMoveCoordinates; } + + public Collection pawnAttackMoveCoordinates(boolean isWhite) { + if (isWhite) { + return WHITE_PAWN_ATTACK_COORDINATES; + } + return BLACK_PAWN_ATTACK_COORDINATES; + } } diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackPositions.java index ff817d8..b31ff84 100644 --- a/src/main/java/chess/domain/player/AttackPositions.java +++ b/src/main/java/chess/domain/player/AttackPositions.java @@ -9,6 +9,8 @@ public class AttackPositions { + private static final int EMPTY = 0; + private final Map counts = new HashMap<>(); public AttackPositions(final Map pieces) { @@ -16,12 +18,26 @@ public AttackPositions(final Map pieces) { .forEach(position -> { Piece piece = pieces.get(position); Collection positions = piece.findAvailableAttackPositions(position); - positions.forEach(target - -> counts.put(target, counts.getOrDefault(target, 0) + 1)); + positions.forEach(this::increase); }); } - public void update(final Position source, final Position target) { -// Collection positions = source.findAvailablePositions(); + public void update(final Position source, final Position target, final Piece piece) { + Collection previousAttackPositions = piece.findAvailableAttackPositions(source); + previousAttackPositions.forEach(this::decrease); + Collection currentAttackPositions = piece.findAvailableAttackPositions(target); + currentAttackPositions.forEach(this::increase); + } + + private Integer increase(final Position target) { + return counts.put(target, counts.getOrDefault(target, 0) + 1); + } + + private Integer decrease(final Position position) { + return counts.put(position, counts.get(position) - 1); + } + + public boolean isEmpty(final Position position) { + return !counts.containsKey(position) || (counts.get(position) == EMPTY); } } diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index 1e041df..a62f368 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -29,12 +29,12 @@ public Set findPaths(final Position source, final Position target) { } public void update(final Position source, final Position target) { - movePiece(source, target); - attackPositions.update(source, target); + Piece sourcePiece = findPieceBy(source); + movePiece(source, target, sourcePiece); + attackPositions.update(source, target, sourcePiece); } - private void movePiece(final Position source, final Position target) { - Piece sourcePiece = findPieceBy(source); + private void movePiece(final Position source, final Position target, final Piece sourcePiece) { pieces.put(target, sourcePiece); pieces.remove(source); } diff --git a/src/test/java/chess/domain/piece/pattern/MovePatternTest.java b/src/test/java/chess/domain/piece/pattern/MovePatternTest.java new file mode 100644 index 0000000..d94db2b --- /dev/null +++ b/src/test/java/chess/domain/piece/pattern/MovePatternTest.java @@ -0,0 +1,59 @@ +package chess.domain.piece.pattern; + +import chess.domain.piece.Color; +import chess.domain.piece.MoveCoordinate; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; +import static chess.domain.piece.MoveCoordinate.NORTH; +import static chess.domain.piece.MoveCoordinate.NORTH_EAST; +import static chess.domain.piece.MoveCoordinate.NORTH_WEST; +import static chess.domain.piece.MoveCoordinate.SOUTH; +import static chess.domain.piece.MoveCoordinate.SOUTH_EAST; +import static chess.domain.piece.MoveCoordinate.SOUTH_WEST; +import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; +import static org.assertj.core.api.Assertions.assertThat; + +class MovePatternTest { + + @Test + @DisplayName("색상에 따라 폰 패턴을 반환한다.") + void pawn_pattern_black() { + // given + Color color = Color.BLACK; + + // when + MovePattern movePattern = MovePattern.pawnPattern(color); + Collection moveCoordinates = movePattern.finiteMoveCoordinates(); + + // then + assertThat(moveCoordinates) + .containsExactlyInAnyOrder( + BLACK_PAWN_INITIAL_SOUTH, + SOUTH_EAST, + SOUTH_WEST, + SOUTH); + } + + @Test + @DisplayName("색상에 따라 폰 패턴을 반환한다.") + void pawn_pattern_white() { + // given + Color color = Color.WHITE; + + // when + MovePattern movePattern = MovePattern.pawnPattern(color); + Collection moveCoordinates = movePattern.finiteMoveCoordinates(); + + // then + assertThat(moveCoordinates) + .containsExactlyInAnyOrder( + WHITE_PAWN_INITIAL_NORTH, + NORTH_EAST, + NORTH_WEST, + NORTH); + } +} diff --git a/src/test/java/chess/domain/player/AttackPositionsTest.java b/src/test/java/chess/domain/player/AttackPositionsTest.java index ab83b88..b639bfe 100644 --- a/src/test/java/chess/domain/player/AttackPositionsTest.java +++ b/src/test/java/chess/domain/player/AttackPositionsTest.java @@ -2,26 +2,53 @@ import chess.domain.board.Position; import chess.domain.piece.Color; +import chess.domain.piece.Knight; import chess.domain.piece.Piece; import chess.domain.piece.PieceFactory; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; + class AttackPositionsTest { - @Test + @ParameterizedTest + @ValueSource(strings = {"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3"}) @DisplayName("기물들이 주어지면 공격 가능한 위치들이 초기화된다.") - void create() { + void create(String key) { //given Map pieces = PieceFactory.createPieces(Color.WHITE); + AttackPositions attackPositions = new AttackPositions(pieces); + Position position = Position.of(key); //when - AttackPositions attackPositions = new AttackPositions(pieces); + boolean isEmpty = attackPositions.isEmpty(position); //then - + assertThat(isEmpty).isFalse(); } + @Test + @DisplayName("시작 위치에서의 공격 가능한 위치들을 없애고, 도착 위치에서의 공격 가능한 위치를 추가한다.") + void update() { + //given + Map pieces = PieceFactory.createPieces(Color.WHITE); + AttackPositions attackPositions = new AttackPositions(pieces); + Position before = Position.of("b1"); + Position current = Position.of("c3"); + + //when + attackPositions.update(before, current, new Knight(Color.WHITE)); + + //then + assertThat(attackPositions.isEmpty(Position.of("a3"))).isFalse(); + assertThat(attackPositions.isEmpty(Position.of("b1"))).isFalse(); + assertThat(attackPositions.isEmpty(Position.of("c3"))).isFalse(); + assertThat(attackPositions.isEmpty(Position.of("b5"))).isFalse(); + assertThat(attackPositions.isEmpty(Position.of("d5"))).isFalse(); + } } From c17c85297d7e6c7ea912a2b8729282ca39e1697e Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Fri, 20 Aug 2021 23:47:36 +0900 Subject: [PATCH 19/42] =?UTF-8?q?feat:=20=ED=82=B9=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=EC=A0=9C=ED=95=9C=EC=A1=B0=EA=B1=B4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?,=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=ED=9D=90=EB=A6=84=20=EC=A0=9C=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++---- .../chess/controller/ChessController.java | 23 +++++++++++-- src/main/java/chess/domain/ChessGame.java | 1 + src/main/java/chess/domain/board/Board.java | 4 +++ src/main/java/chess/domain/piece/Piece.java | 9 ++---- .../domain/piece/mapper/PieceMappers.java | 3 +- .../chess/domain/player/AttackPositions.java | 4 +++ src/main/java/chess/domain/player/Player.java | 8 +++++ .../java/chess/view/ConsoleInputView.java | 4 +-- .../java/chess/view/ConsoleOutputView.java | 11 +++++++ src/main/java/chess/view/OutputView.java | 2 ++ .../java/chess/domain/board/BoardTest.java | 24 ++++++++++++++ .../java/chess/domain/piece/PieceTest.java | 14 +++++++- .../java/chess/domain/player/PlayerTest.java | 32 +++++++++++++++++++ 14 files changed, 132 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index bb1e4a7..4cf8095 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,23 @@ ## 구현해야할 목록 -- [] 입력 - - [x] 시작 여부 입력 +- [x] 입력 + - [x] 명령어 입력 - [] 출력 + - [x] 시작 안내 문구 출력 - [x] 체스판 전체 출력 + - [ ] 점수와 결과 출력 -- [ ] 체스게임(ChessGame) +- [x] 체스게임(ChessGame) - [x] 체스판 - - [] 현재 플레이어 + - [x] 현재 플레이어 - [x] 게임 시작&종료 상태 제어 - 기능 - [x] 명령어 검증 및 처리 - [x] start : 게임 실행 - [x] end : 게임 종료 - - [] move : 인자로 전달받은 source 위치에서 target 위치로 기물 이동 + - [x] move : 인자로 전달받은 source 위치에서 target 위치로 기물 이동 - [x] 체스판(Board) - [x] 체스판 @@ -95,5 +97,5 @@ - [x] 킹 - [x] 모든 방향 1칸 이동 - [x] ERROR : 킹 이동 패턴으로 이동할 수 없는 위치일 경우 - - [ ] 상대의 공격 범위로는 이동 불가능 + - [x] 상대의 공격 범위로는 이동 불가능 diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index f9a587b..afab9a1 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -17,13 +17,30 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { + outputView.printGuide(); ChessGame chessGame = new ChessGame(); while (chessGame.isRunning()) { - Command command = new Command(inputView.getCommand()); + try { + Command command = new Command(inputView.getCommand()); + runCommand(chessGame, command); + printBoard(chessGame); + } catch (UnsupportedOperationException e) { + System.out.println(e.getMessage()); + } + } + } + + private void runCommand(ChessGame chessGame, Command command) { + try { chessGame.run(command); - BoardDto boardDto = new BoardDto(chessGame.getBoard()); - outputView.printBoard(boardDto); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); } } + + private void printBoard(ChessGame chessGame) { + BoardDto boardDto = new BoardDto(chessGame.getBoard()); + outputView.printBoard(boardDto); + } } diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java index 305244f..656ed90 100644 --- a/src/main/java/chess/domain/ChessGame.java +++ b/src/main/java/chess/domain/ChessGame.java @@ -25,6 +25,7 @@ public void run(final Command command) { if (command.isMove()) { move(command.getMoveParameters()); + return; } throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index ad5ef27..8c3a513 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -26,6 +26,10 @@ public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) validateSamePosition(source, target); validateTarget(player, target); + if (player.hasKingOn(source) && enemy.canAttack(target)) { + throw new IllegalArgumentException("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); + } + movePiece(player, source, target); } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 26aad3a..6357690 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -8,9 +8,6 @@ import java.util.Set; import java.util.stream.Collectors; -import static chess.domain.piece.Color.BLACK; -import static chess.domain.piece.Color.WHITE; - public abstract class Piece { protected final MovePattern movePattern; @@ -22,11 +19,11 @@ public abstract class Piece { } public boolean isWhite() { - return color == WHITE; + return color.isWhite(); } - public boolean isBlack() { - return color == BLACK; + public boolean isKing() { + return this instanceof King; } public Set findPath(final Position source, final Position target) { diff --git a/src/main/java/chess/domain/piece/mapper/PieceMappers.java b/src/main/java/chess/domain/piece/mapper/PieceMappers.java index cc53e31..25023de 100644 --- a/src/main/java/chess/domain/piece/mapper/PieceMappers.java +++ b/src/main/java/chess/domain/piece/mapper/PieceMappers.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.NoSuchElementException; public class PieceMappers { @@ -28,7 +27,7 @@ public static String findNameBy(final Piece piece) { .filter(mapper -> mapper.supports(piece)) .map(mapper -> mapper.findNameBy(piece)) .findAny() - .orElseThrow(() -> new NoSuchElementException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); } private PieceMappers() { diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackPositions.java index b31ff84..66a4ed8 100644 --- a/src/main/java/chess/domain/player/AttackPositions.java +++ b/src/main/java/chess/domain/player/AttackPositions.java @@ -40,4 +40,8 @@ private Integer decrease(final Position position) { public boolean isEmpty(final Position position) { return !counts.containsKey(position) || (counts.get(position) == EMPTY); } + + public boolean contains(Position target) { + return counts.containsKey(target); + } } diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index a62f368..deaf4dc 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -46,4 +46,12 @@ public Piece findPieceBy(final Position position) { return pieces.get(position); } + + public boolean hasKingOn(Position position) { + return findPieceBy(position).isKing(); + } + + public boolean canAttack(Position position) { + return attackPositions.contains(position); + } } diff --git a/src/main/java/chess/view/ConsoleInputView.java b/src/main/java/chess/view/ConsoleInputView.java index 2f63d25..67a2f31 100644 --- a/src/main/java/chess/view/ConsoleInputView.java +++ b/src/main/java/chess/view/ConsoleInputView.java @@ -3,13 +3,11 @@ import java.util.Scanner; public class ConsoleInputView implements InputView { + private static final Scanner scanner = new Scanner(System.in); - private static final String HEADER = "> "; @Override public String getCommand() { - System.out.println(HEADER + "체스 게임을 시작합니다."); - System.out.println(HEADER + "게임 시작은 start, 종료는 end 명령을 입력하세요."); String input = scanner.nextLine(); validateNull(input); return input.trim(); diff --git a/src/main/java/chess/view/ConsoleOutputView.java b/src/main/java/chess/view/ConsoleOutputView.java index 3733513..b20ba82 100644 --- a/src/main/java/chess/view/ConsoleOutputView.java +++ b/src/main/java/chess/view/ConsoleOutputView.java @@ -4,6 +4,16 @@ public class ConsoleOutputView implements OutputView { + private static final String HEADER = "> "; + + @Override + public void printGuide() { + System.out.println(HEADER + "체스 게임을 실행합니다."); + System.out.println(HEADER + "게임 시작 : start"); + System.out.println(HEADER + "게임 종료 : end"); + System.out.println(HEADER + "게임 이동 : move source위치 target위치 - 예. move b2 b3"); + } + @Override public void printBoard(final BoardDto boardDto) { boardDto.getPositionDtos() @@ -13,5 +23,6 @@ public void printBoard(final BoardDto boardDto) { System.out.println(); } }); + System.out.println(); } } diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java index 1040482..7185de0 100644 --- a/src/main/java/chess/view/OutputView.java +++ b/src/main/java/chess/view/OutputView.java @@ -3,5 +3,7 @@ import chess.controller.dto.BoardDto; public interface OutputView { + void printGuide(); + void printBoard(BoardDto boardDto); } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 71e6d55..37d805b 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -105,4 +105,28 @@ void move_invalid_paths(String sourcePosition, String targetPosition, boolean is .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) .withMessage("기물을 통과하여 이동할 수 없습니다."); } + + @ParameterizedTest + @CsvSource({"e2, d2", "e2, e1"}) + @DisplayName("킹 도착지를 상대방이 공격 가능한 경우 예외가 발생한다.") + void move_king_invalid_target(String source, String target) { + //given + Board board = setBoardToAttackKing(); + MoveParameters moveParameters = new MoveParameters(Position.of(source), Position.of(target)); + + //when, then + assertThatIllegalArgumentException() + .isThrownBy(() -> board.move(moveParameters, true)) + .withMessage("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); + } + + private Board setBoardToAttackKing() { + Board board = new Board(); + board.move(new MoveParameters(Position.of("e2"), Position.of("e4")), true); + board.move(new MoveParameters(Position.of("d2"), Position.of("d4")), true); + board.move(new MoveParameters(Position.of("e1"), Position.of("e2")), true); + board.move(new MoveParameters(Position.of("c7"), Position.of("c5")), false); + board.move(new MoveParameters(Position.of("d8"), Position.of("a5")), false); + return board; + } } diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java index cb607b1..345017a 100644 --- a/src/test/java/chess/domain/piece/PieceTest.java +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -1,6 +1,7 @@ package chess.domain.piece; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -8,7 +9,6 @@ public class PieceTest { - @ParameterizedTest @CsvSource({"WHITE, true", "BLACK, false"}) @DisplayName("색상을 인자로 받아 객체를 생성한다.") @@ -19,4 +19,16 @@ void create(Color color, boolean expected) { //then assertThat(piece.isWhite()).isEqualTo(expected); } + + @Test + @DisplayName("킹인지 확인한다.") + void is_king() { + // given + Piece king = new King(Color.WHITE); + Piece queen = new Queen(Color.WHITE); + + // when, then + assertThat(king.isKing()).isTrue(); + assertThat(queen.isKing()).isFalse(); + } } diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java index 260e86c..9689e09 100644 --- a/src/test/java/chess/domain/player/PlayerTest.java +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -76,4 +76,36 @@ void find_paths(Color color, String sourcePosition, String targetPosition, Strin //then assertThat(paths).containsOnly(path); } + + @ParameterizedTest + @CsvSource({"WHITE, e1, e2", "BLACK, e8, e7"}) + @DisplayName("주어진 위치에 킹이 있는지 확인한다.") + void has_king_on(Color color, String kingPosition, String notKingPosition) { + // given + Player player = new Player(color); + + // when + boolean isKing = player.hasKingOn(Position.of(kingPosition)); + boolean isNotKing = player.hasKingOn(Position.of(notKingPosition)); + + // then + assertThat(isKing).isTrue(); + assertThat(isNotKing).isFalse(); + } + + @ParameterizedTest + @CsvSource({"WHITE, b3, e6", "BLACK, e6, b3"}) + @DisplayName("주어진 위치를 공격할 수 있는지 확인한다.") + void can_attack(Color color, String attackPosition, String notAttackPosition) { + // given + Player player = new Player(color); + + // when + boolean can = player.canAttack(Position.of(attackPosition)); + boolean cannot = player.canAttack(Position.of(notAttackPosition)); + + // then + assertThat(can).isTrue(); + assertThat(cannot).isFalse(); + } } From 5d69a6766fe5b9c80383dbf418d6bc238ee3ab36 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Sat, 21 Aug 2021 01:30:26 +0900 Subject: [PATCH 20/42] =?UTF-8?q?feat:=20=EC=A0=90=EC=88=98=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/board/Board.java | 21 +++++++-- src/main/java/chess/domain/board/Status.java | 19 ++++++++ src/main/java/chess/domain/piece/Piece.java | 5 ++ .../domain/piece/mapper/BishopMapper.java | 3 +- .../chess/domain/piece/mapper/KingMapper.java | 3 +- .../domain/piece/mapper/KnightMapper.java | 3 +- .../chess/domain/piece/mapper/PawnMapper.java | 13 +++++- .../domain/piece/mapper/PieceMapper.java | 8 +++- .../domain/piece/mapper/PieceMappers.java | 8 ++++ .../domain/piece/mapper/QueenMapper.java | 3 +- .../chess/domain/piece/mapper/RookMapper.java | 3 +- .../chess/domain/player/AttackPositions.java | 24 ++++++---- src/main/java/chess/domain/player/Player.java | 46 +++++++++++++++++++ .../java/chess/domain/board/BoardTest.java | 23 ++++++++++ .../domain/piece/mapper/PieceMappersTest.java | 30 ++++++++++-- .../java/chess/domain/player/PlayerTest.java | 14 ++++++ 16 files changed, 203 insertions(+), 23 deletions(-) create mode 100644 src/main/java/chess/domain/board/Status.java diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index 8c3a513..68153c5 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -25,11 +25,9 @@ public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) validateSourceOwner(enemy, source); validateSamePosition(source, target); validateTarget(player, target); + validateKingMovable(player, enemy, source, target); - if (player.hasKingOn(source) && enemy.canAttack(target)) { - throw new IllegalArgumentException("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); - } - + enemy.attacked(target); movePiece(player, source, target); } @@ -58,6 +56,12 @@ private void validateTarget(final Player player, final Position target) { } } + private void validateKingMovable(final Player player, final Player enemy, final Position source, final Position target) { + if (player.hasKingOn(source) && enemy.canAttack(target)) { + throw new IllegalArgumentException("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); + } + } + private void movePiece(final Player player, final Position source, final Position target) { Set paths = player.findPaths(source, target); validatePathsEmpty(paths); @@ -84,7 +88,14 @@ public Piece findBy(final Position position) { return black.findPieceBy(position); } - public boolean isEmpty(Position position) { + public boolean isEmpty(final Position position) { return !white.hasPieceOn(position) && !black.hasPieceOn(position); } + + public Status getStatus() { + double whiteScore = white.sumScores(); + double blackScore = black.sumScores(); + + return new Status(whiteScore, blackScore); + } } diff --git a/src/main/java/chess/domain/board/Status.java b/src/main/java/chess/domain/board/Status.java new file mode 100644 index 0000000..730c931 --- /dev/null +++ b/src/main/java/chess/domain/board/Status.java @@ -0,0 +1,19 @@ +package chess.domain.board; + +public class Status { + private final double whiteScore; + private final double blackScore; + + public Status(final double whiteScore, final double blackScore) { + this.whiteScore = whiteScore; + this.blackScore = blackScore; + } + + public double getWhiteScore() { + return whiteScore; + } + + public double getBlackScore() { + return blackScore; + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 6357690..abfbd5f 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -26,6 +26,10 @@ public boolean isKing() { return this instanceof King; } + public boolean isPawn() { + return this instanceof Pawn; + } + public Set findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); @@ -50,3 +54,4 @@ public Collection findAvailableAttackPositions(final Position position return positions; } } + diff --git a/src/main/java/chess/domain/piece/mapper/BishopMapper.java b/src/main/java/chess/domain/piece/mapper/BishopMapper.java index f7512df..6b06dc8 100644 --- a/src/main/java/chess/domain/piece/mapper/BishopMapper.java +++ b/src/main/java/chess/domain/piece/mapper/BishopMapper.java @@ -6,9 +6,10 @@ public class BishopMapper extends PieceMapper { private static final String NAME = "b"; + private static final double SCORE = 3; public BishopMapper() { - super(NAME); + super(NAME, SCORE); } @Override diff --git a/src/main/java/chess/domain/piece/mapper/KingMapper.java b/src/main/java/chess/domain/piece/mapper/KingMapper.java index 9b38e06..e8d9904 100644 --- a/src/main/java/chess/domain/piece/mapper/KingMapper.java +++ b/src/main/java/chess/domain/piece/mapper/KingMapper.java @@ -6,9 +6,10 @@ public class KingMapper extends PieceMapper { private static final String NAME = "k"; + private static final double SCORE = 0; public KingMapper() { - super(NAME); + super(NAME, SCORE); } @Override diff --git a/src/main/java/chess/domain/piece/mapper/KnightMapper.java b/src/main/java/chess/domain/piece/mapper/KnightMapper.java index af07be6..6937fbd 100644 --- a/src/main/java/chess/domain/piece/mapper/KnightMapper.java +++ b/src/main/java/chess/domain/piece/mapper/KnightMapper.java @@ -6,9 +6,10 @@ public class KnightMapper extends PieceMapper { private static final String NAME = "n"; + private static final double SCORE = 2.5; public KnightMapper() { - super(NAME); + super(NAME, SCORE); } @Override diff --git a/src/main/java/chess/domain/piece/mapper/PawnMapper.java b/src/main/java/chess/domain/piece/mapper/PawnMapper.java index 7b416aa..4beffec 100644 --- a/src/main/java/chess/domain/piece/mapper/PawnMapper.java +++ b/src/main/java/chess/domain/piece/mapper/PawnMapper.java @@ -6,9 +6,20 @@ public class PawnMapper extends PieceMapper { private static final String NAME = "p"; + private static final int DUPLICATION_THRESHOLD = 1; + private static final double SCORE = 1; + private static final double SCORE_ON_DUPLICATION = 0.5; public PawnMapper() { - super(NAME); + super(NAME, SCORE); + } + + public static double calculate(final int count) { + if (count > DUPLICATION_THRESHOLD) { + return count * SCORE_ON_DUPLICATION; + } + + return SCORE; } @Override diff --git a/src/main/java/chess/domain/piece/mapper/PieceMapper.java b/src/main/java/chess/domain/piece/mapper/PieceMapper.java index 1880424..700aa07 100644 --- a/src/main/java/chess/domain/piece/mapper/PieceMapper.java +++ b/src/main/java/chess/domain/piece/mapper/PieceMapper.java @@ -4,9 +4,11 @@ public abstract class PieceMapper { private final String name; + private final double score; - PieceMapper(final String name) { + PieceMapper(final String name, final double score) { this.name = name; + this.score = score; } abstract boolean supports(Piece piece); @@ -18,4 +20,8 @@ public String findNameBy(Piece piece) { return name.toUpperCase(); } + + public double getScore() { + return score; + } } diff --git a/src/main/java/chess/domain/piece/mapper/PieceMappers.java b/src/main/java/chess/domain/piece/mapper/PieceMappers.java index 25023de..f69c56b 100644 --- a/src/main/java/chess/domain/piece/mapper/PieceMappers.java +++ b/src/main/java/chess/domain/piece/mapper/PieceMappers.java @@ -30,6 +30,14 @@ public static String findNameBy(final Piece piece) { .orElseThrow(() -> new IllegalArgumentException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); } + public static double findScoreBy(final Piece piece) { + return pieceMappers.stream() + .filter(mapper -> mapper.supports(piece)) + .map(PieceMapper::getScore) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); + } + private PieceMappers() { } } diff --git a/src/main/java/chess/domain/piece/mapper/QueenMapper.java b/src/main/java/chess/domain/piece/mapper/QueenMapper.java index bf20c2a..1251829 100644 --- a/src/main/java/chess/domain/piece/mapper/QueenMapper.java +++ b/src/main/java/chess/domain/piece/mapper/QueenMapper.java @@ -6,9 +6,10 @@ public class QueenMapper extends PieceMapper { private static final String NAME = "q"; + private static final double SCORE = 9; public QueenMapper() { - super(NAME); + super(NAME, SCORE); } @Override diff --git a/src/main/java/chess/domain/piece/mapper/RookMapper.java b/src/main/java/chess/domain/piece/mapper/RookMapper.java index 9f38c6d..c7239b2 100644 --- a/src/main/java/chess/domain/piece/mapper/RookMapper.java +++ b/src/main/java/chess/domain/piece/mapper/RookMapper.java @@ -6,9 +6,10 @@ public class RookMapper extends PieceMapper { private static final String NAME = "r"; + private static final double SCORE = 5; public RookMapper() { - super(NAME); + super(NAME, SCORE); } @Override diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackPositions.java index 66a4ed8..b02d974 100644 --- a/src/main/java/chess/domain/player/AttackPositions.java +++ b/src/main/java/chess/domain/player/AttackPositions.java @@ -23,25 +23,33 @@ public AttackPositions(final Map pieces) { } public void update(final Position source, final Position target, final Piece piece) { - Collection previousAttackPositions = piece.findAvailableAttackPositions(source); + remove(source, piece); + add(target, piece); + } + + public void remove(final Position position, final Piece piece) { + Collection previousAttackPositions = piece.findAvailableAttackPositions(position); previousAttackPositions.forEach(this::decrease); - Collection currentAttackPositions = piece.findAvailableAttackPositions(target); + } + + private void add(final Position position, final Piece piece) { + Collection currentAttackPositions = piece.findAvailableAttackPositions(position); currentAttackPositions.forEach(this::increase); } - private Integer increase(final Position target) { - return counts.put(target, counts.getOrDefault(target, 0) + 1); + private void decrease(final Position position) { + counts.put(position, counts.get(position) - 1); } - private Integer decrease(final Position position) { - return counts.put(position, counts.get(position) - 1); + private void increase(final Position position) { + counts.put(position, counts.getOrDefault(position, 0) + 1); } public boolean isEmpty(final Position position) { return !counts.containsKey(position) || (counts.get(position) == EMPTY); } - public boolean contains(Position target) { - return counts.containsKey(target); + public boolean contains(final Position position) { + return counts.containsKey(position); } } diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index deaf4dc..d6ef108 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -1,13 +1,19 @@ package chess.domain.player; +import chess.domain.board.File; import chess.domain.board.Position; import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.piece.PieceFactory; +import chess.domain.piece.mapper.PawnMapper; +import chess.domain.piece.mapper.PieceMappers; +import java.util.EnumMap; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; public class Player { @@ -19,6 +25,37 @@ public Player(final Color color) { attackPositions = new AttackPositions(pieces); } + public double sumScores() { + List pawnPositions = pieces.keySet() + .stream() + .filter(position -> { + Piece piece = pieces.get(position); + return piece.isPawn(); + }) + .collect(Collectors.toList()); + + double pawnScores = calculatePawnScores(pawnPositions); + double sum = pieces.values() + .stream() + .filter(piece -> !piece.isPawn()) + .mapToDouble(PieceMappers::findScoreBy) + .sum(); + return pawnScores + sum; + } + + private double calculatePawnScores(final List pawnPositions) { + Map pawnCount = new EnumMap<>(File.class); + + pawnPositions.stream() + .map(Position::getFile) + .forEach(file -> pawnCount.put(file, pawnCount.getOrDefault(file, 0) + 1)); + + return pawnCount.values() + .stream() + .mapToDouble(PawnMapper::calculate) + .sum(); + } + public boolean hasPieceOn(final Position position) { return pieces.containsKey(position); } @@ -54,4 +91,13 @@ public boolean hasKingOn(Position position) { public boolean canAttack(Position position) { return attackPositions.contains(position); } + + public void attacked(final Position target) { + if (!hasPieceOn(target)) { + return; + } + + attackPositions.remove(target, pieces.get(target)); + pieces.remove(target); + } } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 37d805b..cbe225a 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -120,6 +120,29 @@ void move_king_invalid_target(String source, String target) { .withMessage("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); } + @Test + @DisplayName("모든 플레이어의 점수를 반환한다.") + void get_status() { + //given + Board board = setBoardToGetStatus(); + + //when + Status status = board.getStatus(); + + //then + assertThat(status.getWhiteScore()).isEqualTo(37); + assertThat(status.getBlackScore()).isEqualTo(37); + } + + private Board setBoardToGetStatus() { + Board board = new Board(); + board.move(new MoveParameters(Position.of("e7"), Position.of("e5")), false); + board.move(new MoveParameters(Position.of("e5"), Position.of("e4")), false); + board.move(new MoveParameters(Position.of("e4"), Position.of("e3")), false); + board.move(new MoveParameters(Position.of("d2"), Position.of("e3")), true); + return board; + } + private Board setBoardToAttackKing() { Board board = new Board(); board.move(new MoveParameters(Position.of("e2"), Position.of("e4")), true); diff --git a/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java b/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java index 6582ff8..b04265f 100644 --- a/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java +++ b/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java @@ -1,8 +1,12 @@ package chess.domain.piece.mapper; +import chess.domain.piece.Bishop; import chess.domain.piece.Color; +import chess.domain.piece.Knight; import chess.domain.piece.Pawn; import chess.domain.piece.Piece; +import chess.domain.piece.Queen; +import chess.domain.piece.Rook; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -15,21 +19,41 @@ public class PieceMappersTest { @ParameterizedTest - @MethodSource("createParams") + @MethodSource("createParamsForName") @DisplayName("피스가 주어지면 해당 피스와 색상에 맞는 이름을 반환한다.") - void find(Piece piece, String expected) { + void find_name_by(Piece piece, String expected) { //given, when String name = PieceMappers.findNameBy(piece); //then assertThat(name).isEqualTo(expected); + } + @ParameterizedTest + @MethodSource("createParamsForScore") + @DisplayName("피스가 주어지면 해당 피스의 점수를 반환한다.") + void get_score(Piece piece, double expected) { + //given, when + double score = PieceMappers.findScoreBy(piece); + + //then + assertThat(score).isEqualTo(expected); } - private static Stream createParams() { + private static Stream createParamsForName() { return Stream.of( Arguments.of(new Pawn(Color.WHITE), "p"), Arguments.of(new Pawn(Color.BLACK), "P") ); } + + private static Stream createParamsForScore() { + return Stream.of( + Arguments.of(new Pawn(Color.WHITE), 1), + Arguments.of(new Knight(Color.WHITE), 2.5), + Arguments.of(new Bishop(Color.WHITE), 3), + Arguments.of(new Rook(Color.WHITE), 5), + Arguments.of(new Queen(Color.WHITE), 9) + ); + } } diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java index 9689e09..ce1d887 100644 --- a/src/test/java/chess/domain/player/PlayerTest.java +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -3,6 +3,7 @@ import chess.domain.board.Position; import chess.domain.piece.Color; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -108,4 +109,17 @@ void can_attack(Color color, String attackPosition, String notAttackPosition) { assertThat(can).isTrue(); assertThat(cannot).isFalse(); } + + @Test + @DisplayName("현재 남아있는 피스의 점수 합을 구한다.") + void sum_scores() { + //given + Player player = new Player(Color.WHITE); + + //when + double sum = player.sumScores(); + + //then + assertThat(sum).isEqualTo(38); + } } From be1d67e9234de3c2fba475571a211b91da1225cd Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Sat, 21 Aug 2021 02:11:30 +0900 Subject: [PATCH 21/42] =?UTF-8?q?feat:=20=EC=A0=90=EC=88=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chess/controller/ChessController.java | 36 +++++++++++-- src/main/java/chess/domain/ChessGame.java | 30 ++++++----- src/main/java/chess/domain/board/Board.java | 6 ++- src/main/java/chess/domain/board/Status.java | 14 +++++- .../java/chess/domain/command/Command.java | 5 ++ src/main/java/chess/domain/player/Player.java | 11 ++-- .../java/chess/view/ConsoleOutputView.java | 29 +++++++++++ src/main/java/chess/view/OutputView.java | 5 ++ src/test/java/chess/domain/ChessGameTest.java | 50 ------------------- .../java/chess/domain/player/PlayerTest.java | 14 ++++++ 10 files changed, 124 insertions(+), 76 deletions(-) delete mode 100644 src/test/java/chess/domain/ChessGameTest.java diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index afab9a1..68dce81 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -2,6 +2,7 @@ import chess.controller.dto.BoardDto; import chess.domain.ChessGame; +import chess.domain.board.Status; import chess.domain.command.Command; import chess.view.InputView; import chess.view.OutputView; @@ -22,6 +23,7 @@ public void run() { while (chessGame.isRunning()) { try { + outputView.printTurn(chessGame.isWhiteTurn()); Command command = new Command(inputView.getCommand()); runCommand(chessGame, command); printBoard(chessGame); @@ -29,18 +31,46 @@ public void run() { System.out.println(e.getMessage()); } } + + printBoard(chessGame); + printStatus(chessGame); } - private void runCommand(ChessGame chessGame, Command command) { + private void runCommand(final ChessGame chessGame, final Command command) { try { - chessGame.run(command); + if (command.isStart()) { + return; + } + + if (command.isEnd()) { + chessGame.end(); + return; + } + + if (command.isMove()) { + chessGame.move(command.getMoveParameters()); + return; + } + + if (command.isStatus()) { + printStatus(chessGame); + return; + } } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); + return; } + + throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); } - private void printBoard(ChessGame chessGame) { + private void printBoard(final ChessGame chessGame) { BoardDto boardDto = new BoardDto(chessGame.getBoard()); outputView.printBoard(boardDto); } + + private void printStatus(final ChessGame chessGame) { + Status status = chessGame.getStatus(); + outputView.printStatus(status); + } } diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java index 656ed90..0828ad7 100644 --- a/src/main/java/chess/domain/ChessGame.java +++ b/src/main/java/chess/domain/ChessGame.java @@ -1,7 +1,7 @@ package chess.domain; import chess.domain.board.Board; -import chess.domain.command.Command; +import chess.domain.board.Status; import chess.domain.command.MoveParameters; public class ChessGame { @@ -13,27 +13,21 @@ public class ChessGame { public ChessGame() { } - public void run(final Command command) { - if (command.isStart()) { - return; - } + public void move(final MoveParameters moveParameters) { + board.move(moveParameters, isWhiteTurn); + isWhiteTurn = !isWhiteTurn; - if (command.isEnd()) { + if (board.isEnd()) { isRunning = false; - return; - } - - if (command.isMove()) { - move(command.getMoveParameters()); - return; } + } - throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); + public void end() { + isRunning = false; } - private void move(final MoveParameters moveParameters) { - board.move(moveParameters, isWhiteTurn); - isWhiteTurn = !isWhiteTurn; + public Status getStatus() { + return board.getStatus(); } public boolean isRunning() { @@ -43,4 +37,8 @@ public boolean isRunning() { public Board getBoard() { return board; } + + public boolean isWhiteTurn() { + return isWhiteTurn; + } } diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index 68153c5..e8b91a3 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -96,6 +96,10 @@ public Status getStatus() { double whiteScore = white.sumScores(); double blackScore = black.sumScores(); - return new Status(whiteScore, blackScore); + return new Status(whiteScore, blackScore, white.isKingDead(), black.isKingDead()); + } + + public boolean isEnd() { + return white.isKingDead() || black.isKingDead(); } } diff --git a/src/main/java/chess/domain/board/Status.java b/src/main/java/chess/domain/board/Status.java index 730c931..b48f253 100644 --- a/src/main/java/chess/domain/board/Status.java +++ b/src/main/java/chess/domain/board/Status.java @@ -3,10 +3,14 @@ public class Status { private final double whiteScore; private final double blackScore; + private final boolean isWhiteKingDead; + private final boolean isBlackKingDead; - public Status(final double whiteScore, final double blackScore) { + public Status(final double whiteScore, final double blackScore, boolean isWhiteKingDead, boolean isBlackKingDead) { this.whiteScore = whiteScore; this.blackScore = blackScore; + this.isWhiteKingDead = isWhiteKingDead; + this.isBlackKingDead = isBlackKingDead; } public double getWhiteScore() { @@ -16,4 +20,12 @@ public double getWhiteScore() { public double getBlackScore() { return blackScore; } + + public boolean isWhiteKingDead() { + return isWhiteKingDead; + } + + public boolean isBlackKingDead() { + return isBlackKingDead; + } } diff --git a/src/main/java/chess/domain/command/Command.java b/src/main/java/chess/domain/command/Command.java index ddea82a..5aff42b 100644 --- a/src/main/java/chess/domain/command/Command.java +++ b/src/main/java/chess/domain/command/Command.java @@ -12,6 +12,7 @@ public class Command { private static final String START = "start"; private static final String END = "end"; private static final String MOVE = "move"; + private static final String STATUS = "status"; private final String command; private final List parameters; @@ -42,6 +43,10 @@ public boolean isMove() { return command.equals(MOVE); } + public boolean isStatus() { + return command.equals(STATUS); + } + public MoveParameters getMoveParameters() { if (parameters.size() != 2) { throw new IllegalArgumentException(); diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index d6ef108..c2acd82 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -8,11 +8,7 @@ import chess.domain.piece.mapper.PawnMapper; import chess.domain.piece.mapper.PieceMappers; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public class Player { @@ -88,6 +84,11 @@ public boolean hasKingOn(Position position) { return findPieceBy(position).isKing(); } + public boolean isKingDead() { + return pieces.values().stream() + .noneMatch(Piece::isKing); + } + public boolean canAttack(Position position) { return attackPositions.contains(position); } diff --git a/src/main/java/chess/view/ConsoleOutputView.java b/src/main/java/chess/view/ConsoleOutputView.java index b20ba82..4720828 100644 --- a/src/main/java/chess/view/ConsoleOutputView.java +++ b/src/main/java/chess/view/ConsoleOutputView.java @@ -1,10 +1,13 @@ package chess.view; import chess.controller.dto.BoardDto; +import chess.domain.board.Status; public class ConsoleOutputView implements OutputView { private static final String HEADER = "> "; + private static final String TURN_FORMAT = HEADER + "%s의 차례입니다.%n"; + private static final String WINNER_FORMAT = HEADER + "%s의 승리입니다. 축하합니다.%n"; @Override public void printGuide() { @@ -25,4 +28,30 @@ public void printBoard(final BoardDto boardDto) { }); System.out.println(); } + + @Override + public void printStatus(final Status status) { + if (status.isWhiteKingDead()) { + System.out.printf(WINNER_FORMAT, "BLACK"); + return; + } + + if (status.isBlackKingDead()) { + System.out.printf(WINNER_FORMAT, "WHITE"); + return; + } + + System.out.println(HEADER + "WHITE 점수: " + status.getWhiteScore()); + System.out.println(HEADER + "BLACK 점수: " + status.getBlackScore()); + } + + @Override + public void printTurn(boolean isWhiteTurn) { + if (isWhiteTurn) { + System.out.printf(TURN_FORMAT, "WHITE"); + return; + } + + System.out.printf(TURN_FORMAT, "BLACK"); + } } diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java index 7185de0..50c37bf 100644 --- a/src/main/java/chess/view/OutputView.java +++ b/src/main/java/chess/view/OutputView.java @@ -1,9 +1,14 @@ package chess.view; import chess.controller.dto.BoardDto; +import chess.domain.board.Status; public interface OutputView { void printGuide(); void printBoard(BoardDto boardDto); + + void printStatus(Status status); + + void printTurn(boolean isWhiteTurn); } diff --git a/src/test/java/chess/domain/ChessGameTest.java b/src/test/java/chess/domain/ChessGameTest.java deleted file mode 100644 index 54a43d9..0000000 --- a/src/test/java/chess/domain/ChessGameTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package chess.domain; - -import chess.domain.command.Command; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class ChessGameTest { - - @Test - @DisplayName("게임 시작 명령어를 입력받을 경우 게임을 시작한다.") - void run_start_command() { - //given - ChessGame chessGame = new ChessGame(); - Command command = new Command("start"); - - //when - chessGame.run(command); - - //then - assertThat(chessGame.isRunning()).isTrue(); - } - - @Test - @DisplayName("게임 종료 명령어를 입력받을 경우 게임을 종료한다.") - void run_end_command() { - //given - ChessGame chessGame = new ChessGame(); - Command command = new Command("end"); - - //when - chessGame.run(command); - - //then - assertThat(chessGame.isRunning()).isFalse(); - } - - @Test - @DisplayName("유효하지 않은 명령어를 입력받을 경우 예외를 던진다.") - void run_invalid_command() { - //given - ChessGame chessGame = new ChessGame(); - Command command = new Command("invalid_command"); - - //when, then - assertThrows(UnsupportedOperationException.class, () -> chessGame.run(command)); - } -} diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java index ce1d887..75882b6 100644 --- a/src/test/java/chess/domain/player/PlayerTest.java +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -122,4 +122,18 @@ void sum_scores() { //then assertThat(sum).isEqualTo(38); } + + @Test + @DisplayName("킹이 존재하는지 반환한다.") + void is_king_dead() { + //given + Player player = new Player(Color.WHITE); + player.attacked(Position.of("e1")); + + //when + boolean kingDead = player.isKingDead(); + + //then + assertThat(kingDead).isTrue(); + } } From aba4a3ad04d7a3f8ec5f3aff9d4a74937ab964cf Mon Sep 17 00:00:00 2001 From: Jiwoo Kim Date: Sat, 21 Aug 2021 03:18:21 +0900 Subject: [PATCH 22/42] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../java/chess/controller/dto/BoardDto.java | 6 +- src/main/java/chess/domain/board/Board.java | 1 + .../chess/domain/command/MoveParameters.java | 2 +- src/main/java/chess/domain/piece/Bishop.java | 2 +- src/main/java/chess/domain/piece/Color.java | 1 - src/main/java/chess/domain/piece/King.java | 2 +- src/main/java/chess/domain/piece/Knight.java | 2 +- src/main/java/chess/domain/piece/Pawn.java | 10 +- src/main/java/chess/domain/piece/Piece.java | 13 +-- .../java/chess/domain/piece/PieceFactory.java | 2 +- src/main/java/chess/domain/piece/Queen.java | 2 +- src/main/java/chess/domain/piece/Rook.java | 2 +- .../domain/piece/mapper/BishopMapper.java | 19 ---- .../chess/domain/piece/mapper/KingMapper.java | 19 ---- .../domain/piece/mapper/KnightMapper.java | 19 ---- .../chess/domain/piece/mapper/PawnMapper.java | 29 ----- .../domain/piece/mapper/PieceMapper.java | 27 ----- .../domain/piece/mapper/PieceMappers.java | 43 ------- .../domain/piece/mapper/QueenMapper.java | 19 ---- .../chess/domain/piece/mapper/RookMapper.java | 19 ---- .../piece/{pattern => type}/MovePattern.java | 33 +----- .../chess/domain/piece/type/PieceType.java | 74 ++++++++++++ .../chess/domain/player/AttackPositions.java | 1 - .../{piece => player}/MoveCoordinate.java | 8 +- src/main/java/chess/domain/player/Player.java | 39 ++++--- .../domain/{board => player}/Position.java | 40 +++---- .../java/chess/domain/board/BoardTest.java | 8 +- .../java/chess/domain/board/PositionTest.java | 40 ------- .../java/chess/domain/piece/BishopTest.java | 2 +- .../java/chess/domain/piece/KingTest.java | 5 +- .../java/chess/domain/piece/KnightTest.java | 5 +- .../java/chess/domain/piece/PawnTest.java | 17 +-- .../java/chess/domain/piece/PieceTest.java | 13 --- .../java/chess/domain/piece/QueenTest.java | 7 +- .../java/chess/domain/piece/RookTest.java | 2 +- .../{pattern => type}/MovePatternTest.java | 25 +---- .../PieceTypeTest.java} | 56 +++++++-- .../domain/player/AttackPositionsTest.java | 5 +- .../java/chess/domain/player/PlayerTest.java | 1 - .../chess/domain/player/PositionTest.java | 106 ++++++++++++++++++ 41 files changed, 323 insertions(+), 407 deletions(-) delete mode 100644 src/main/java/chess/domain/piece/mapper/BishopMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/KingMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/KnightMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/PawnMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/PieceMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/PieceMappers.java delete mode 100644 src/main/java/chess/domain/piece/mapper/QueenMapper.java delete mode 100644 src/main/java/chess/domain/piece/mapper/RookMapper.java rename src/main/java/chess/domain/piece/{pattern => type}/MovePattern.java (75%) create mode 100644 src/main/java/chess/domain/piece/type/PieceType.java rename src/main/java/chess/domain/{piece => player}/MoveCoordinate.java (92%) rename src/main/java/chess/domain/{board => player}/Position.java (81%) delete mode 100644 src/test/java/chess/domain/board/PositionTest.java rename src/test/java/chess/domain/piece/{pattern => type}/MovePatternTest.java (50%) rename src/test/java/chess/domain/piece/{mapper/PieceMappersTest.java => type/PieceTypeTest.java} (56%) create mode 100644 src/test/java/chess/domain/player/PositionTest.java diff --git a/README.md b/README.md index 4cf8095..3cb26bf 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ - [x] 입력 - [x] 명령어 입력 -- [] 출력 +- [x] 출력 - [x] 시작 안내 문구 출력 - [x] 체스판 전체 출력 - - [ ] 점수와 결과 출력 + - [x] 점수와 결과 출력 - [x] 체스게임(ChessGame) - [x] 체스판 diff --git a/src/main/java/chess/controller/dto/BoardDto.java b/src/main/java/chess/controller/dto/BoardDto.java index 31dd52c..a014e34 100644 --- a/src/main/java/chess/controller/dto/BoardDto.java +++ b/src/main/java/chess/controller/dto/BoardDto.java @@ -2,10 +2,10 @@ import chess.domain.board.Board; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.board.Rank; import chess.domain.piece.Piece; -import chess.domain.piece.mapper.PieceMappers; +import chess.domain.piece.type.PieceType; +import chess.domain.player.Position; import java.util.ArrayList; import java.util.Arrays; @@ -30,7 +30,7 @@ private void addPositionDto(final File file, final Rank rank, final Board board) } Piece piece = board.findBy(position); - String name = PieceMappers.findNameBy(piece); + String name = PieceType.findNameBy(piece); PositionDto positionDto = new PositionDto(position.getFile(), name); positionDtos.add(positionDto); } diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index e8b91a3..f0f7109 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -4,6 +4,7 @@ import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.player.Player; +import chess.domain.player.Position; import java.util.Set; diff --git a/src/main/java/chess/domain/command/MoveParameters.java b/src/main/java/chess/domain/command/MoveParameters.java index 198b266..c8a607a 100644 --- a/src/main/java/chess/domain/command/MoveParameters.java +++ b/src/main/java/chess/domain/command/MoveParameters.java @@ -1,6 +1,6 @@ package chess.domain.command; -import chess.domain.board.Position; +import chess.domain.player.Position; import java.util.List; diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 6265e9c..e6a7a6a 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; public class Bishop extends Piece { diff --git a/src/main/java/chess/domain/piece/Color.java b/src/main/java/chess/domain/piece/Color.java index f61d576..c83450c 100644 --- a/src/main/java/chess/domain/piece/Color.java +++ b/src/main/java/chess/domain/piece/Color.java @@ -6,6 +6,5 @@ public enum Color { public boolean isWhite() { return this == WHITE; - } } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index 855d923..f3c7b73 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; public class King extends Piece { diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index 4ed9f31..14e65ee 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; public class Knight extends Piece { diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index f72e35c..0d23f38 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,17 +1,15 @@ package chess.domain.piece; -import chess.domain.board.Position; import chess.domain.board.Rank; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; +import chess.domain.player.MoveCoordinate; +import chess.domain.player.Position; import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; -import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; -import static chess.domain.piece.MoveCoordinate.NORTH; -import static chess.domain.piece.MoveCoordinate.SOUTH; -import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; +import static chess.domain.player.MoveCoordinate.*; public class Pawn extends Piece { diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index abfbd5f..1f72110 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,7 +1,8 @@ package chess.domain.piece; -import chess.domain.board.Position; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; +import chess.domain.player.MoveCoordinate; +import chess.domain.player.Position; import java.util.Collection; import java.util.HashSet; @@ -22,14 +23,6 @@ public boolean isWhite() { return color.isWhite(); } - public boolean isKing() { - return this instanceof King; - } - - public boolean isPawn() { - return this instanceof Pawn; - } - public Set findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java index b3d23d3..4ee6f22 100644 --- a/src/main/java/chess/domain/piece/PieceFactory.java +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -1,8 +1,8 @@ package chess.domain.piece; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.board.Rank; +import chess.domain.player.Position; import java.util.Arrays; import java.util.HashMap; diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 7389d31..9c68242 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; public class Queen extends Piece { diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index 2e9f2c6..2a7bba0 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.piece.pattern.MovePattern; +import chess.domain.piece.type.MovePattern; public class Rook extends Piece { diff --git a/src/main/java/chess/domain/piece/mapper/BishopMapper.java b/src/main/java/chess/domain/piece/mapper/BishopMapper.java deleted file mode 100644 index 6b06dc8..0000000 --- a/src/main/java/chess/domain/piece/mapper/BishopMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Bishop; -import chess.domain.piece.Piece; - -public class BishopMapper extends PieceMapper { - - private static final String NAME = "b"; - private static final double SCORE = 3; - - public BishopMapper() { - super(NAME, SCORE); - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof Bishop; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/KingMapper.java b/src/main/java/chess/domain/piece/mapper/KingMapper.java deleted file mode 100644 index e8d9904..0000000 --- a/src/main/java/chess/domain/piece/mapper/KingMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.King; -import chess.domain.piece.Piece; - -public class KingMapper extends PieceMapper { - - private static final String NAME = "k"; - private static final double SCORE = 0; - - public KingMapper() { - super(NAME, SCORE); - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof King; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/KnightMapper.java b/src/main/java/chess/domain/piece/mapper/KnightMapper.java deleted file mode 100644 index 6937fbd..0000000 --- a/src/main/java/chess/domain/piece/mapper/KnightMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Knight; -import chess.domain.piece.Piece; - -public class KnightMapper extends PieceMapper { - - private static final String NAME = "n"; - private static final double SCORE = 2.5; - - public KnightMapper() { - super(NAME, SCORE); - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof Knight; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/PawnMapper.java b/src/main/java/chess/domain/piece/mapper/PawnMapper.java deleted file mode 100644 index 4beffec..0000000 --- a/src/main/java/chess/domain/piece/mapper/PawnMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Pawn; -import chess.domain.piece.Piece; - -public class PawnMapper extends PieceMapper { - - private static final String NAME = "p"; - private static final int DUPLICATION_THRESHOLD = 1; - private static final double SCORE = 1; - private static final double SCORE_ON_DUPLICATION = 0.5; - - public PawnMapper() { - super(NAME, SCORE); - } - - public static double calculate(final int count) { - if (count > DUPLICATION_THRESHOLD) { - return count * SCORE_ON_DUPLICATION; - } - - return SCORE; - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof Pawn; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/PieceMapper.java b/src/main/java/chess/domain/piece/mapper/PieceMapper.java deleted file mode 100644 index 700aa07..0000000 --- a/src/main/java/chess/domain/piece/mapper/PieceMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Piece; - -public abstract class PieceMapper { - private final String name; - private final double score; - - PieceMapper(final String name, final double score) { - this.name = name; - this.score = score; - } - - abstract boolean supports(Piece piece); - - public String findNameBy(Piece piece) { - if (piece.isWhite()) { - return name; - } - - return name.toUpperCase(); - } - - public double getScore() { - return score; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/PieceMappers.java b/src/main/java/chess/domain/piece/mapper/PieceMappers.java deleted file mode 100644 index f69c56b..0000000 --- a/src/main/java/chess/domain/piece/mapper/PieceMappers.java +++ /dev/null @@ -1,43 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Piece; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -public class PieceMappers { - - private static final Collection pieceMappers = createMappers(); - - private static Collection createMappers() { - return Collections.unmodifiableCollection( - Arrays.asList( - new PawnMapper(), - new KnightMapper(), - new BishopMapper(), - new RookMapper(), - new QueenMapper(), - new KingMapper() - )); - } - - public static String findNameBy(final Piece piece) { - return pieceMappers.stream() - .filter(mapper -> mapper.supports(piece)) - .map(mapper -> mapper.findNameBy(piece)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); - } - - public static double findScoreBy(final Piece piece) { - return pieceMappers.stream() - .filter(mapper -> mapper.supports(piece)) - .map(PieceMapper::getScore) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("해당 기물에 해당하는 매퍼가 존재하지 않습니다.")); - } - - private PieceMappers() { - } -} diff --git a/src/main/java/chess/domain/piece/mapper/QueenMapper.java b/src/main/java/chess/domain/piece/mapper/QueenMapper.java deleted file mode 100644 index 1251829..0000000 --- a/src/main/java/chess/domain/piece/mapper/QueenMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Piece; -import chess.domain.piece.Queen; - -public class QueenMapper extends PieceMapper { - - private static final String NAME = "q"; - private static final double SCORE = 9; - - public QueenMapper() { - super(NAME, SCORE); - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof Queen; - } -} diff --git a/src/main/java/chess/domain/piece/mapper/RookMapper.java b/src/main/java/chess/domain/piece/mapper/RookMapper.java deleted file mode 100644 index c7239b2..0000000 --- a/src/main/java/chess/domain/piece/mapper/RookMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Piece; -import chess.domain.piece.Rook; - -public class RookMapper extends PieceMapper { - - private static final String NAME = "r"; - private static final double SCORE = 5; - - public RookMapper() { - super(NAME, SCORE); - } - - @Override - public boolean supports(final Piece piece) { - return piece instanceof Rook; - } -} diff --git a/src/main/java/chess/domain/piece/pattern/MovePattern.java b/src/main/java/chess/domain/piece/type/MovePattern.java similarity index 75% rename from src/main/java/chess/domain/piece/pattern/MovePattern.java rename to src/main/java/chess/domain/piece/type/MovePattern.java index 9f82c26..9c926da 100644 --- a/src/main/java/chess/domain/piece/pattern/MovePattern.java +++ b/src/main/java/chess/domain/piece/type/MovePattern.java @@ -1,32 +1,11 @@ -package chess.domain.piece.pattern; +package chess.domain.piece.type; import chess.domain.piece.Color; -import chess.domain.piece.MoveCoordinate; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; -import static chess.domain.piece.MoveCoordinate.EAST; -import static chess.domain.piece.MoveCoordinate.NORTH; -import static chess.domain.piece.MoveCoordinate.NORTH_EAST; -import static chess.domain.piece.MoveCoordinate.NORTH_EAST_LEFT; -import static chess.domain.piece.MoveCoordinate.NORTH_EAST_RIGHT; -import static chess.domain.piece.MoveCoordinate.NORTH_WEST; -import static chess.domain.piece.MoveCoordinate.NORTH_WEST_LEFT; -import static chess.domain.piece.MoveCoordinate.NORTH_WEST_RIGHT; -import static chess.domain.piece.MoveCoordinate.SOUTH; -import static chess.domain.piece.MoveCoordinate.SOUTH_EAST; -import static chess.domain.piece.MoveCoordinate.SOUTH_EAST_LEFT; -import static chess.domain.piece.MoveCoordinate.SOUTH_EAST_RIGHT; -import static chess.domain.piece.MoveCoordinate.SOUTH_WEST; -import static chess.domain.piece.MoveCoordinate.SOUTH_WEST_LEFT; -import static chess.domain.piece.MoveCoordinate.SOUTH_WEST_RIGHT; -import static chess.domain.piece.MoveCoordinate.WEST; -import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; +import chess.domain.player.MoveCoordinate; + +import java.util.*; + +import static chess.domain.player.MoveCoordinate.*; public class MovePattern { diff --git a/src/main/java/chess/domain/piece/type/PieceType.java b/src/main/java/chess/domain/piece/type/PieceType.java new file mode 100644 index 0000000..e62961b --- /dev/null +++ b/src/main/java/chess/domain/piece/type/PieceType.java @@ -0,0 +1,74 @@ +package chess.domain.piece.type; + +import chess.domain.piece.*; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum PieceType { + + KING("k", 0), + QUEEN("q", 9), + ROOK("r", 5), + BISHOP("b", 3), + KNIGHT("n", 2.5), + PAWN("p", 1); + + private static final Map, PieceType> PIECE_TYPE_MAP = createPieceTypeMap(); + private static final int DUPLICATION_THRESHOLD = 1; + private static final double PAWN_SCORE_ON_DUPLICATION = 0.5; + private final String name; + private final double score; + + PieceType(final String name, final double score) { + this.name = name; + this.score = score; + } + + private static Map, PieceType> createPieceTypeMap() { + Map, PieceType> map = new HashMap<>(); + map.put(King.class, KING); + map.put(Queen.class, QUEEN); + map.put(Rook.class, ROOK); + map.put(Bishop.class, BISHOP); + map.put(Knight.class, KNIGHT); + map.put(Pawn.class, PAWN); + return Collections.unmodifiableMap(map); + } + + public static PieceType of(Piece piece) { + return PIECE_TYPE_MAP.get(piece.getClass()); + } + + public static String findNameBy(Piece piece) { + PieceType pieceType = PieceType.of(piece); + + if (piece.isWhite()) { + return pieceType.name; + } + + return pieceType.name.toUpperCase(); + } + + public static double findScoreBy(Piece piece) { + PieceType pieceType = PieceType.of(piece); + return pieceType.score; + } + + public static boolean isKing(Piece piece) { + return PieceType.of(piece) == KING; + } + + public static boolean isPawn(Piece piece) { + return PieceType.of(piece) == PAWN; + } + + public static double sumPawnScores(final int count) { + if (count > DUPLICATION_THRESHOLD) { + return count * PAWN_SCORE_ON_DUPLICATION; + } + + return PAWN.score; + } +} diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackPositions.java index b02d974..2007382 100644 --- a/src/main/java/chess/domain/player/AttackPositions.java +++ b/src/main/java/chess/domain/player/AttackPositions.java @@ -1,6 +1,5 @@ package chess.domain.player; -import chess.domain.board.Position; import chess.domain.piece.Piece; import java.util.Collection; diff --git a/src/main/java/chess/domain/piece/MoveCoordinate.java b/src/main/java/chess/domain/player/MoveCoordinate.java similarity index 92% rename from src/main/java/chess/domain/piece/MoveCoordinate.java rename to src/main/java/chess/domain/player/MoveCoordinate.java index 7cc93d0..59545f0 100644 --- a/src/main/java/chess/domain/piece/MoveCoordinate.java +++ b/src/main/java/chess/domain/player/MoveCoordinate.java @@ -1,6 +1,4 @@ -package chess.domain.piece; - -import static java.lang.Math.abs; +package chess.domain.player; public enum MoveCoordinate { NORTH_EAST(1, 1), @@ -64,10 +62,6 @@ private boolean hasSameSign(final int fileGap, final int rankGap) { return (file ^ fileGap) >= 0 && (rank ^ rankGap) >= 0; } - public int countMove() { - return abs(this.file) + abs(this.rank); - } - public int getFile() { return file; } diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index c2acd82..4710c52 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -1,12 +1,10 @@ package chess.domain.player; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.piece.PieceFactory; -import chess.domain.piece.mapper.PawnMapper; -import chess.domain.piece.mapper.PieceMappers; +import chess.domain.piece.type.PieceType; import java.util.*; import java.util.stream.Collectors; @@ -22,33 +20,40 @@ public Player(final Color color) { } public double sumScores() { - List pawnPositions = pieces.keySet() + List pawnPositions = findPawnPositions(); + + double pawnScores = calculatePawnScores(pawnPositions); + double scoresExceptPawn = calculateScoresExceptPawn(); + return pawnScores + scoresExceptPawn; + } + + private List findPawnPositions() { + return pieces.keySet() .stream() .filter(position -> { Piece piece = pieces.get(position); - return piece.isPawn(); + return PieceType.isPawn(piece); }) .collect(Collectors.toList()); - - double pawnScores = calculatePawnScores(pawnPositions); - double sum = pieces.values() - .stream() - .filter(piece -> !piece.isPawn()) - .mapToDouble(PieceMappers::findScoreBy) - .sum(); - return pawnScores + sum; } private double calculatePawnScores(final List pawnPositions) { Map pawnCount = new EnumMap<>(File.class); - pawnPositions.stream() .map(Position::getFile) .forEach(file -> pawnCount.put(file, pawnCount.getOrDefault(file, 0) + 1)); return pawnCount.values() .stream() - .mapToDouble(PawnMapper::calculate) + .mapToDouble(PieceType::sumPawnScores) + .sum(); + } + + private double calculateScoresExceptPawn() { + return pieces.values() + .stream() + .filter(piece -> !PieceType.isPawn(piece)) + .mapToDouble(PieceType::findScoreBy) .sum(); } @@ -81,12 +86,12 @@ public Piece findPieceBy(final Position position) { } public boolean hasKingOn(Position position) { - return findPieceBy(position).isKing(); + return PieceType.isKing(findPieceBy(position)); } public boolean isKingDead() { return pieces.values().stream() - .noneMatch(Piece::isKing); + .noneMatch(PieceType::isKing); } public boolean canAttack(Position position) { diff --git a/src/main/java/chess/domain/board/Position.java b/src/main/java/chess/domain/player/Position.java similarity index 81% rename from src/main/java/chess/domain/board/Position.java rename to src/main/java/chess/domain/player/Position.java index 8b32741..ee973b6 100644 --- a/src/main/java/chess/domain/board/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -1,14 +1,9 @@ -package chess.domain.board; +package chess.domain.player; -import chess.domain.piece.MoveCoordinate; +import chess.domain.board.File; +import chess.domain.board.Rank; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; public class Position { private static final Map POSITIONS = createPositions(); @@ -17,7 +12,8 @@ private static Map createPositions() { Map positions = new LinkedHashMap<>(); Arrays.stream(File.values()) - .forEach(file -> Arrays.stream(Rank.values()).forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); + .forEach(file -> Arrays.stream(Rank.values()) + .forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); return positions; } @@ -42,20 +38,12 @@ private Position(final File file, final Rank rank) { this.rank = rank; } - public File getFile() { - return file; - } - - public Rank getRank() { - return rank; - } - - public int calculateFileGap(final Position source) { - return file.calculateGap(source.getFile()); + public int calculateFileGap(final Position position) { + return file.calculateGap(position.getFile()); } - public int calculateRankGap(final Position source) { - return rank.calculateGap(source.getRank()); + public int calculateRankGap(final Position position) { + return rank.calculateGap(position.getRank()); } public Set findPassingPositions(Position target, MoveCoordinate moveCoordinate) { @@ -110,4 +98,12 @@ private Position move(final MoveCoordinate moveCoordinate) { public boolean hasSameRank(Rank rank) { return this.rank == rank; } + + public File getFile() { + return file; + } + + public Rank getRank() { + return rank; + } } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index cbe225a..2585f02 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,7 +1,7 @@ package chess.domain.board; import chess.domain.command.MoveParameters; -import chess.domain.piece.Pawn; +import chess.domain.player.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -23,12 +23,12 @@ void create() { Board board = new Board(); //then - assertThat(board.findBy(pawnPosition)).isInstanceOf(Pawn.class); + assertThat(board.isEmpty(pawnPosition)).isFalse(); assertThat(board.isEmpty(emptyPosition)).isTrue(); } @Test - @DisplayName("인자로 받은 시작 위치에 기물이 존재하지 않을 경우 예외가 발생한다.") + @DisplayName("시작 위치에 기물이 존재하지 않을 경우 예외가 발생한다.") void move_source_position_empty() { //given Board board = new Board(); @@ -108,7 +108,7 @@ void move_invalid_paths(String sourcePosition, String targetPosition, boolean is @ParameterizedTest @CsvSource({"e2, d2", "e2, e1"}) - @DisplayName("킹 도착지를 상대방이 공격 가능한 경우 예외가 발생한다.") + @DisplayName("상대방이 킹의 목적지를 공격 가능한 경우 예외가 발생한다.") void move_king_invalid_target(String source, String target) { //given Board board = setBoardToAttackKing(); diff --git a/src/test/java/chess/domain/board/PositionTest.java b/src/test/java/chess/domain/board/PositionTest.java deleted file mode 100644 index e30fada..0000000 --- a/src/test/java/chess/domain/board/PositionTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package chess.domain.board; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class PositionTest { - - @Test - @DisplayName("가로, 세로 인자에 해당하는 위치를 반환한다.") - void from_file_and_rank() { - //given - File file = File.a; - Rank rank = Rank.R1; - - //when - Position position = Position.from(file, rank); - - //then - assertThat(position).extracting("file", "rank") - .containsOnly(file, rank); - } - - @Test - @DisplayName("문자열 키에 해당하는 위치를 반환한다.") - void from_key() { - //given - File file = File.a; - Rank rank = Rank.R1; - String key = file.name() + rank.getIndex(); - - //when - Position position = Position.of(key); - - //then - assertThat(position).extracting("file", "rank") - .containsOnly(file, rank); - } -} diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index e8080f3..64eb8b5 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -1,8 +1,8 @@ package chess.domain.piece; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.board.Rank; +import chess.domain.player.Position; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java index ce71a38..2dc762e 100644 --- a/src/test/java/chess/domain/piece/KingTest.java +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.board.Position; +import chess.domain.player.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -41,7 +41,8 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new King(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @Test diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java index 3979525..2629bcc 100644 --- a/src/test/java/chess/domain/piece/KnightTest.java +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.board.Position; +import chess.domain.player.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -41,7 +41,8 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new Knight(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @Test diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java index 16993f1..3d702ed 100644 --- a/src/test/java/chess/domain/piece/PawnTest.java +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -1,6 +1,6 @@ package chess.domain.piece; -import chess.domain.board.Position; +import chess.domain.player.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -16,8 +16,7 @@ class PawnTest { @ParameterizedTest - @CsvSource({"b2, b3, WHITE", "b2, a3, WHITE", "b2, c3, WHITE", - "b7, b6, BLACK", "b7, a6, BLACK", "b7, c6, BLACK"}) + @CsvSource({"b2, b3, WHITE", "b7, b6, BLACK"}) @DisplayName("최초 이동 시 1칸 전진한다.") void find_paths_success_move_count_one_on_initial_move(String sourcePosition, String targetPosition, Color color) { //given @@ -75,12 +74,12 @@ void find_paths_fail_move_invalid_count_on_initial_move(String sourcePosition, S Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @ParameterizedTest - @CsvSource({"d4, d6, WHITE", "d4, c6, WHITE", "d4, e6, WHITE", - "d4, d2, BLACK", "d4, c2, BLACK", "d4, e2, BLACK"}) + @CsvSource({"d4, d6, WHITE", "d4, d2, BLACK"}) @DisplayName("최초 이동이 아닌 경우 1칸 초과 전진하면 예외가 발생한다.") void find_paths_fail_move_invalid_count(String sourcePosition, String targetPosition, Color color) { //given @@ -89,7 +88,8 @@ void find_paths_fail_move_invalid_count(String sourcePosition, String targetPosi Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @ParameterizedTest @@ -102,7 +102,8 @@ void find_paths_fail_move_backward(String sourcePosition, String targetPosition, Piece piece = new Pawn(color); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @Test diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java index 345017a..e4659fe 100644 --- a/src/test/java/chess/domain/piece/PieceTest.java +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -1,7 +1,6 @@ package chess.domain.piece; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -19,16 +18,4 @@ void create(Color color, boolean expected) { //then assertThat(piece.isWhite()).isEqualTo(expected); } - - @Test - @DisplayName("킹인지 확인한다.") - void is_king() { - // given - Piece king = new King(Color.WHITE); - Piece queen = new Queen(Color.WHITE); - - // when, then - assertThat(king.isKing()).isTrue(); - assertThat(queen.isKing()).isFalse(); - } } diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java index b1f85e6..67b1ca9 100644 --- a/src/test/java/chess/domain/piece/QueenTest.java +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -1,8 +1,8 @@ package chess.domain.piece; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.board.Rank; +import chess.domain.player.Position; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -42,7 +42,7 @@ void find_paths_success_diagonal(String targetPosition, Tuple expected) { @ParameterizedTest @ValueSource(strings = {"d5", "c4", "e4", "d3"}) @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") - void find_paths_success_straight(String targetPosition) { + void find_paths_success_cardinal(String targetPosition) { //given Position source = Position.of("d4"); Position target = Position.of(targetPosition); @@ -65,7 +65,8 @@ void find_paths_invalid_target(String invalidTarget) { Piece piece = new Queen(Color.WHITE); //when //then - assertThatIllegalArgumentException().isThrownBy(() -> piece.findPath(source, target)); + assertThatIllegalArgumentException() + .isThrownBy(() -> piece.findPath(source, target)); } @Test diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java index a787a58..4331349 100644 --- a/src/test/java/chess/domain/piece/RookTest.java +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -1,8 +1,8 @@ package chess.domain.piece; import chess.domain.board.File; -import chess.domain.board.Position; import chess.domain.board.Rank; +import chess.domain.player.Position; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/domain/piece/pattern/MovePatternTest.java b/src/test/java/chess/domain/piece/type/MovePatternTest.java similarity index 50% rename from src/test/java/chess/domain/piece/pattern/MovePatternTest.java rename to src/test/java/chess/domain/piece/type/MovePatternTest.java index d94db2b..a45bb0e 100644 --- a/src/test/java/chess/domain/piece/pattern/MovePatternTest.java +++ b/src/test/java/chess/domain/piece/type/MovePatternTest.java @@ -1,20 +1,13 @@ -package chess.domain.piece.pattern; +package chess.domain.piece.type; import chess.domain.piece.Color; -import chess.domain.piece.MoveCoordinate; +import chess.domain.player.MoveCoordinate; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Collection; -import static chess.domain.piece.MoveCoordinate.BLACK_PAWN_INITIAL_SOUTH; -import static chess.domain.piece.MoveCoordinate.NORTH; -import static chess.domain.piece.MoveCoordinate.NORTH_EAST; -import static chess.domain.piece.MoveCoordinate.NORTH_WEST; -import static chess.domain.piece.MoveCoordinate.SOUTH; -import static chess.domain.piece.MoveCoordinate.SOUTH_EAST; -import static chess.domain.piece.MoveCoordinate.SOUTH_WEST; -import static chess.domain.piece.MoveCoordinate.WHITE_PAWN_INITIAL_NORTH; +import static chess.domain.player.MoveCoordinate.*; import static org.assertj.core.api.Assertions.assertThat; class MovePatternTest { @@ -31,11 +24,7 @@ void pawn_pattern_black() { // then assertThat(moveCoordinates) - .containsExactlyInAnyOrder( - BLACK_PAWN_INITIAL_SOUTH, - SOUTH_EAST, - SOUTH_WEST, - SOUTH); + .containsExactlyInAnyOrder(BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH); } @Test @@ -50,10 +39,6 @@ void pawn_pattern_white() { // then assertThat(moveCoordinates) - .containsExactlyInAnyOrder( - WHITE_PAWN_INITIAL_NORTH, - NORTH_EAST, - NORTH_WEST, - NORTH); + .containsExactlyInAnyOrder(WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH); } } diff --git a/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java b/src/test/java/chess/domain/piece/type/PieceTypeTest.java similarity index 56% rename from src/test/java/chess/domain/piece/mapper/PieceMappersTest.java rename to src/test/java/chess/domain/piece/type/PieceTypeTest.java index b04265f..8c1acd7 100644 --- a/src/test/java/chess/domain/piece/mapper/PieceMappersTest.java +++ b/src/test/java/chess/domain/piece/type/PieceTypeTest.java @@ -1,13 +1,8 @@ -package chess.domain.piece.mapper; - -import chess.domain.piece.Bishop; -import chess.domain.piece.Color; -import chess.domain.piece.Knight; -import chess.domain.piece.Pawn; -import chess.domain.piece.Piece; -import chess.domain.piece.Queen; -import chess.domain.piece.Rook; +package chess.domain.piece.type; + +import chess.domain.piece.*; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -16,14 +11,27 @@ import static org.assertj.core.api.Assertions.assertThat; -public class PieceMappersTest { +public class PieceTypeTest { + + @Test + @DisplayName("피스에 해당하는 피스 타입을 반환한다.") + void of() { + // given + Piece piece = new Pawn(Color.WHITE); + + // when + PieceType pieceType = PieceType.of(piece); + + // then + assertThat(pieceType).isSameAs(PieceType.PAWN); + } @ParameterizedTest @MethodSource("createParamsForName") @DisplayName("피스가 주어지면 해당 피스와 색상에 맞는 이름을 반환한다.") void find_name_by(Piece piece, String expected) { //given, when - String name = PieceMappers.findNameBy(piece); + String name = PieceType.findNameBy(piece); //then assertThat(name).isEqualTo(expected); @@ -34,12 +42,36 @@ void find_name_by(Piece piece, String expected) { @DisplayName("피스가 주어지면 해당 피스의 점수를 반환한다.") void get_score(Piece piece, double expected) { //given, when - double score = PieceMappers.findScoreBy(piece); + double score = PieceType.findScoreBy(piece); //then assertThat(score).isEqualTo(expected); } + @Test + @DisplayName("킹인지 확인한다.") + void is_king() { + // given + Piece king = new King(Color.WHITE); + Piece queen = new Queen(Color.WHITE); + + // when, then + assertThat(PieceType.isKing(king)).isTrue(); + assertThat(PieceType.isKing(queen)).isFalse(); + } + + @Test + @DisplayName("폰인지 확인한다.") + void is_pawn() { + // given + Piece pawn = new Pawn(Color.WHITE); + Piece queen = new Queen(Color.WHITE); + + // when, then + assertThat(PieceType.isPawn(pawn)).isTrue(); + assertThat(PieceType.isPawn(queen)).isFalse(); + } + private static Stream createParamsForName() { return Stream.of( Arguments.of(new Pawn(Color.WHITE), "p"), diff --git a/src/test/java/chess/domain/player/AttackPositionsTest.java b/src/test/java/chess/domain/player/AttackPositionsTest.java index b639bfe..bc9dc11 100644 --- a/src/test/java/chess/domain/player/AttackPositionsTest.java +++ b/src/test/java/chess/domain/player/AttackPositionsTest.java @@ -1,6 +1,5 @@ package chess.domain.player; -import chess.domain.board.Position; import chess.domain.piece.Color; import chess.domain.piece.Knight; import chess.domain.piece.Piece; @@ -18,7 +17,7 @@ class AttackPositionsTest { @ParameterizedTest @ValueSource(strings = {"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3"}) - @DisplayName("기물들이 주어지면 공격 가능한 위치들이 초기화된다.") + @DisplayName("공격 가능한 위치들을 표시한다.") void create(String key) { //given Map pieces = PieceFactory.createPieces(Color.WHITE); @@ -33,7 +32,7 @@ void create(String key) { } @Test - @DisplayName("시작 위치에서의 공격 가능한 위치들을 없애고, 도착 위치에서의 공격 가능한 위치를 추가한다.") + @DisplayName("공격 가능한 위치들을 갱신한다.") void update() { //given Map pieces = PieceFactory.createPieces(Color.WHITE); diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java index 75882b6..56d64aa 100644 --- a/src/test/java/chess/domain/player/PlayerTest.java +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -1,6 +1,5 @@ package chess.domain.player; -import chess.domain.board.Position; import chess.domain.piece.Color; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/chess/domain/player/PositionTest.java b/src/test/java/chess/domain/player/PositionTest.java new file mode 100644 index 0000000..8c83bbc --- /dev/null +++ b/src/test/java/chess/domain/player/PositionTest.java @@ -0,0 +1,106 @@ +package chess.domain.player; + +import chess.domain.board.File; +import chess.domain.board.Rank; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class PositionTest { + + @Test + @DisplayName("가로, 세로 인자에 해당하는 위치를 반환한다.") + void from_file_and_rank() { + //given + File file = File.a; + Rank rank = Rank.R1; + + //when + Position position = Position.from(file, rank); + + //then + assertThat(position).extracting("file", "rank") + .containsOnly(file, rank); + } + + @Test + @DisplayName("문자열 키에 해당하는 위치를 반환한다.") + void from_key() { + //given + File file = File.a; + Rank rank = Rank.R1; + String key = file.name() + rank.getIndex(); + + //when + Position position = Position.of(key); + + //then + assertThat(position).extracting("file", "rank") + .containsOnly(file, rank); + } + + @Test + @DisplayName("거쳐가는 모든 위치를 반환한다.") + void find_passing_positions() { + // given + Position source = Position.of("d4"); + Position target = Position.of("d7"); + MoveCoordinate moveCoordinate = MoveCoordinate.NORTH; + + // when + Set positions = source.findPassingPositions(target, moveCoordinate); + + // then + assertThat(positions) + .hasSize(2) + .containsExactlyInAnyOrder(Position.of("d5"), Position.of("d6")); + } + + @ParameterizedTest + @MethodSource("createParams") + @DisplayName("이동 가능한 위치를 반환한다.") + void find_available_positions(boolean isFinite, Collection expected) { + // given + Position position = Position.of("d4"); + MoveCoordinate moveCoordinate = MoveCoordinate.EAST; + + // when + Collection positions = position.findAvailablePositions(moveCoordinate, isFinite); + + // then + assertThat(positions) + .hasSize(expected.size()) + .containsAll(expected); + } + + @Test + @DisplayName("랭크가 동일한지 확인한다.") + void has_same_rank() { + // given + Rank rank = Rank.R1; + Position position = Position.of("a1"); + + // when + boolean hasSameRank = position.hasSameRank(rank); + + // then + assertThat(hasSameRank).isTrue(); + } + + private static Stream createParams() { + return Stream.of( + Arguments.of(false, Arrays.asList(Position.of("e4"), Position.of("f4"), Position.of("g4"), Position.of("h4"))), + Arguments.of(true, Collections.singletonList(Position.of("e4"))) + ); + } +} From 1f8194b59db6b7757354880c0d823423413816f0 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Tue, 31 Aug 2021 23:51:11 +0900 Subject: [PATCH 23/42] =?UTF-8?q?refactor:=20LinkedHashMap=20->=20HashMap?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/player/Position.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/chess/domain/player/Position.java b/src/main/java/chess/domain/player/Position.java index ee973b6..c69aad5 100644 --- a/src/main/java/chess/domain/player/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -3,13 +3,19 @@ import chess.domain.board.File; import chess.domain.board.Rank; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class Position { private static final Map POSITIONS = createPositions(); private static Map createPositions() { - Map positions = new LinkedHashMap<>(); + Map positions = new HashMap<>(); Arrays.stream(File.values()) .forEach(file -> Arrays.stream(Rank.values()) From de1c9c7d9a8216e9641ab90df879e541df69f4e8 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Wed, 1 Sep 2021 00:31:18 +0900 Subject: [PATCH 24/42] =?UTF-8?q?refactor:=20Position=20=EC=BA=90=EC=8B=9C?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EC=BD=94=EB=93=9C=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EC=B6=9C=ED=95=98=EC=97=AC=20=EA=B0=80?= =?UTF-8?q?=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/player/Position.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/chess/domain/player/Position.java b/src/main/java/chess/domain/player/Position.java index c69aad5..11b4b6d 100644 --- a/src/main/java/chess/domain/player/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; public class Position { private static final Map POSITIONS = createPositions(); @@ -18,12 +19,21 @@ private static Map createPositions() { Map positions = new HashMap<>(); Arrays.stream(File.values()) - .forEach(file -> Arrays.stream(Rank.values()) - .forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); + .forEach(put(positions)); return positions; } + private static Consumer put(final Map positions) { + return file -> Arrays.stream(Rank.values()) + .map(rank -> new Position(file, rank)) + .forEach(position -> positions.put(createKey(position), position)); + } + + private static String createKey(final Position position) { + return createKey(position.file, position.rank); + } + private static String createKey(final File file, final Rank rank) { return file.name() + rank.getIndex(); } From 09575d28155d4f00052489b697ef5a2a2c4c88af Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Wed, 1 Sep 2021 00:43:57 +0900 Subject: [PATCH 25/42] =?UTF-8?q?refactor:=20=EB=B6=80=EC=A0=95=EC=97=B0?= =?UTF-8?q?=EC=82=B0=EC=9E=90=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/player/Position.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/chess/domain/player/Position.java b/src/main/java/chess/domain/player/Position.java index 11b4b6d..895b6fc 100644 --- a/src/main/java/chess/domain/player/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -62,11 +62,11 @@ public int calculateRankGap(final Position position) { return rank.calculateGap(position.getRank()); } - public Set findPassingPositions(Position target, MoveCoordinate moveCoordinate) { + public Set findPassingPositions(final Position target, final MoveCoordinate moveCoordinate) { Set positions = new HashSet<>(); Position current = this; - while (!target.equals(current)) { + while (target.isDifferent(current)) { current = current.move(moveCoordinate); positions.add(current); } @@ -75,24 +75,30 @@ public Set findPassingPositions(Position target, MoveCoordinate moveCo return positions; } + private boolean isDifferent(final Position current) { + return !this.equals(current); + } + public Collection findAvailablePositions(final MoveCoordinate moveCoordinate, final boolean isFinite) { if (isFinite) { return getFinitePositions(moveCoordinate); } + return getInfinitePositions(moveCoordinate); } - private Collection getFinitePositions(MoveCoordinate moveCoordinate) { + private Collection getFinitePositions(final MoveCoordinate moveCoordinate) { if (isMovable(moveCoordinate)) { return Collections.singleton(move(moveCoordinate)); } + return Collections.emptySet(); } - private Collection getInfinitePositions(MoveCoordinate moveCoordinate) { + private Collection getInfinitePositions(final MoveCoordinate moveCoordinate) { Collection positions = new HashSet<>(); - Position current = this; + while (current.isMovable(moveCoordinate)) { current = current.move(moveCoordinate); positions.add(current); @@ -106,12 +112,12 @@ private boolean isMovable(final MoveCoordinate moveCoordinate) { } private Position move(final MoveCoordinate moveCoordinate) { - File file = this.file.add(moveCoordinate.getFile()); - Rank rank = this.rank.add(moveCoordinate.getRank()); - return Position.from(file, rank); + File movedFile = this.file.add(moveCoordinate.getFile()); + Rank movedRank = this.rank.add(moveCoordinate.getRank()); + return Position.from(movedFile, movedRank); } - public boolean hasSameRank(Rank rank) { + public boolean hasSameRank(final Rank rank) { return this.rank == rank; } From b1e17a0fe1b32535fb380038c562d0ff59888421 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Wed, 1 Sep 2021 00:51:55 +0900 Subject: [PATCH 26/42] =?UTF-8?q?refactor:=20=EA=B0=80=EB=8F=85=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20=EB=B0=8F=20'=3D=3D'=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/piece/PieceFactory.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java index 4ee6f22..0ee167e 100644 --- a/src/main/java/chess/domain/piece/PieceFactory.java +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -16,8 +16,8 @@ public class PieceFactory { private PieceFactory() { } - public static Map createPieces(Color color) { - if (color == WHITE) { + public static Map createPieces(final Color color) { + if (color.isWhite()) { return whitePieces(); } return blackPieces(); @@ -33,7 +33,12 @@ private static Map blackPieces() { private static Map initializePieces(final Rank rank, final Rank pawnRank, final Color color) { Map board = new HashMap<>(); + initializePiecesExceptForPawns(rank, color, board); + initializePawns(pawnRank, color, board); + return board; + } + private static void initializePiecesExceptForPawns(final Rank rank, final Color color, final Map board) { board.put(Position.from(File.a, rank), new Rook(color)); board.put(Position.from(File.b, rank), new Knight(color)); board.put(Position.from(File.c, rank), new Bishop(color)); @@ -42,10 +47,10 @@ private static Map initializePieces(final Rank rank, final Rank board.put(Position.from(File.f, rank), new Bishop(color)); board.put(Position.from(File.g, rank), new Knight(color)); board.put(Position.from(File.h, rank), new Rook(color)); + } + private static void initializePawns(final Rank pawnRank, final Color color, final Map board) { Arrays.stream(File.values()) .forEach(file -> board.put(Position.from(file, pawnRank), new Pawn(color))); - - return board; } } From 527e5d98c99ff32f33f2e9a61be8877e4c6bdf01 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Wed, 1 Sep 2021 16:53:28 +0900 Subject: [PATCH 27/42] =?UTF-8?q?refactor:=20ENUM=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99(=EB=8C=80=EB=AC=B8=EC=9E=90=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9)=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/board/File.java | 33 ++++---- .../java/chess/domain/board/FileTest.java | 79 +++++++++++++++++++ 2 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 src/test/java/chess/domain/board/FileTest.java diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java index 8a18a09..145c1dd 100644 --- a/src/main/java/chess/domain/board/File.java +++ b/src/main/java/chess/domain/board/File.java @@ -3,14 +3,14 @@ import java.util.Arrays; public enum File { - a(1), - b(2), - c(3), - d(4), - e(5), - f(6), - g(7), - h(8); + A(1), + B(2), + C(3), + D(4), + E(5), + F(6), + G(7), + H(8); private final int index; @@ -22,27 +22,28 @@ public static File of(final int fileIndex) { return Arrays.stream(File.values()) .filter(file -> hasSameIndex(fileIndex, file)) .findAny() - .orElseThrow(IllegalArgumentException::new); + .orElseThrow(() -> new IllegalArgumentException("일치하는 파일이 존재하지 않습니다.")); } private static boolean hasSameIndex(final int fileIndex, final File file) { - return file.getIndex() == fileIndex; - } - - public int getIndex() { - return index; + return file.index == fileIndex; } public int calculateGap(final File file) { return this.index - file.index; } - public File add(final int amount) { + public File move(final int amount) { return File.of(this.index + amount); } public boolean canMove(final int amount) { int fileIndex = index + amount; - return fileIndex >= a.index && fileIndex <= h.index; + + return isInRange(fileIndex); + } + + private boolean isInRange(final int fileIndex) { + return fileIndex >= A.index && fileIndex <= H.index; } } diff --git a/src/test/java/chess/domain/board/FileTest.java b/src/test/java/chess/domain/board/FileTest.java new file mode 100644 index 0000000..0da1d2b --- /dev/null +++ b/src/test/java/chess/domain/board/FileTest.java @@ -0,0 +1,79 @@ +package chess.domain.board; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class FileTest { + + @ParameterizedTest + @CsvSource(value = {"1, A", "8, H"}) + @DisplayName("일치하는 파일 객체를 반환한다.") + void of(int fileIndex, File expected) { + //when + File file = File.of(fileIndex); + + //then + assertThat(file).isEqualTo(expected); + } + + @ParameterizedTest + @ValueSource(ints = {0, 9}) + @DisplayName("일치하는 파일을 찾지 못할 경우 예외가 발생한다.") + void of(int fileIndex) { + //when //then + assertThatIllegalArgumentException() + .isThrownBy(() -> File.of(fileIndex)) + .withMessage("일치하는 파일이 존재하지 않습니다."); + } + + @Test + @DisplayName("다른 파일의 위치 값을 뺀 값을 반환한다.") + void calculateGap() { + //given + int index1 = 8; + int index2 = 1; + File file1 = File.of(index1); + File file2 = File.of(index2); + + //when + int result = file1.calculateGap(file2); + + //then + assertThat(result).isEqualTo(index1 - index2); + } + + @Test + @DisplayName("이동한 위치의 파일을 반환한다.") + void move() { + //given + int index1 = 1; + int index2 = 7; + File file = File.of(index1); + + //when + File movedFile = file.move(index2); + + //then + assertThat(movedFile).isEqualTo(File.of(index1 + index2)); + } + + @ParameterizedTest + @CsvSource(value = {"1, 7, true", "1, 8, false"}) + @DisplayName("이동하려는 위치가 이동 범위에 있는 지 확인한다.") + void canMove(int fileIndex, int moveAmount, boolean expected) { + //given + File file = File.of(fileIndex); + + //when + boolean actual = file.canMove(moveAmount); + + //then + assertThat(actual).isEqualTo(expected); + } +} From 770f4cbd59e81ad2ef658f570347fc66c8da6919 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Wed, 1 Sep 2021 17:01:06 +0900 Subject: [PATCH 28/42] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/board/Rank.java | 15 ++-- .../java/chess/domain/board/RankTest.java | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/test/java/chess/domain/board/RankTest.java diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java index 549e78e..9c67b1e 100644 --- a/src/main/java/chess/domain/board/Rank.java +++ b/src/main/java/chess/domain/board/Rank.java @@ -20,13 +20,13 @@ public enum Rank { public static Rank of(final int rankIndex) { return Arrays.stream(Rank.values()) - .filter(rank -> hasSameIndex(rankIndex, rank)) + .filter(rank -> rank.hasSameIndex(rankIndex)) .findAny() - .orElseThrow(IllegalArgumentException::new); + .orElseThrow(() -> new IllegalArgumentException("일치하는 랭크가 존재하지 않습니다.")); } - private static boolean hasSameIndex(final int rankIndex, final Rank rank) { - return rank.index == rankIndex; + private boolean hasSameIndex(final int rankIndex) { + return this.index == rankIndex; } public int getIndex() { @@ -37,12 +37,17 @@ public int calculateGap(final Rank rank) { return this.index - rank.index; } - public Rank add(final int amount) { + public Rank move(final int amount) { return Rank.of(this.index + amount); } public boolean canMove(final int amount) { int rankIndex = index + amount; + + return isInRange(rankIndex); + } + + private boolean isInRange(final int rankIndex) { return rankIndex >= R1.index && rankIndex <= R8.index; } } diff --git a/src/test/java/chess/domain/board/RankTest.java b/src/test/java/chess/domain/board/RankTest.java new file mode 100644 index 0000000..012406e --- /dev/null +++ b/src/test/java/chess/domain/board/RankTest.java @@ -0,0 +1,79 @@ +package chess.domain.board; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class RankTest { + + @ParameterizedTest + @CsvSource(value = {"1, R1", "8, R8"}) + @DisplayName("일치하는 랭크 객체를 반환한다.") + void of(int rankIndex, Rank expected) { + //when + Rank rank = Rank.of(rankIndex); + + //then + assertThat(rank).isEqualTo(expected); + } + + @ParameterizedTest + @ValueSource(ints = {0, 9}) + @DisplayName("일치하는 랭크를 찾지 못할 경우 예외가 발생한다.") + void of(int rankIndex) { + //when //then + assertThatIllegalArgumentException() + .isThrownBy(() -> Rank.of(rankIndex)) + .withMessage("일치하는 랭크가 존재하지 않습니다."); + } + + @Test + @DisplayName("다른 랭크의 위치 값을 뺀 값을 반환한다.") + void calculateGap() { + //given + int index1 = 8; + int index2 = 1; + Rank rank1 = Rank.of(index1); + Rank rank2 = Rank.of(index2); + + //when + int result = rank1.calculateGap(rank2); + + //then + assertThat(result).isEqualTo(index1 - index2); + } + + @Test + @DisplayName("이동한 위치의 랭크를 반환한다.") + void move() { + //given + int index1 = 1; + int index2 = 7; + Rank rank = Rank.of(index1); + + //when + Rank movedRank = rank.move(index2); + + //then + assertThat(movedRank).isEqualTo(Rank.of(index1 + index2)); + } + + @ParameterizedTest + @CsvSource(value = {"1, 7, true", "1, 8, false"}) + @DisplayName("이동하려는 위치가 이동 범위에 있는 지 확인한다.") + void canMove(int rankIndex, int moveAmount, boolean expected) { + //given + Rank rank = Rank.of(rankIndex); + + //when + boolean actual = rank.canMove(moveAmount); + + //then + assertThat(actual).isEqualTo(expected); + } +} From 2df66a6266b573baee27d9158c2cc7c9a9528bab Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 2 Sep 2021 22:35:22 +0900 Subject: [PATCH 29/42] =?UTF-8?q?refactor:=20ENUM=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20map=20=EC=9E=90=EB=A3=8C=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/controller/dto/BoardDto.java | 4 +- .../chess/controller/dto/PositionDto.java | 2 +- src/main/java/chess/domain/board/File.java | 27 +++++-- src/main/java/chess/domain/board/Rank.java | 27 +++++-- .../chess/domain/piece/PieceResolver.java | 72 ++++++++++++++++++ .../chess/domain/piece/type/PieceType.java | 74 ------------------- ...ceTypeTest.java => PieceResolverTest.java} | 39 ++-------- 7 files changed, 122 insertions(+), 123 deletions(-) create mode 100644 src/main/java/chess/domain/piece/PieceResolver.java delete mode 100644 src/main/java/chess/domain/piece/type/PieceType.java rename src/test/java/chess/domain/piece/{type/PieceTypeTest.java => PieceResolverTest.java} (63%) diff --git a/src/main/java/chess/controller/dto/BoardDto.java b/src/main/java/chess/controller/dto/BoardDto.java index a014e34..78a66db 100644 --- a/src/main/java/chess/controller/dto/BoardDto.java +++ b/src/main/java/chess/controller/dto/BoardDto.java @@ -4,7 +4,7 @@ import chess.domain.board.File; import chess.domain.board.Rank; import chess.domain.piece.Piece; -import chess.domain.piece.type.PieceType; +import chess.domain.piece.PieceResolver; import chess.domain.player.Position; import java.util.ArrayList; @@ -30,7 +30,7 @@ private void addPositionDto(final File file, final Rank rank, final Board board) } Piece piece = board.findBy(position); - String name = PieceType.findNameBy(piece); + String name = PieceResolver.findNameBy(piece); PositionDto positionDto = new PositionDto(position.getFile(), name); positionDtos.add(positionDto); } diff --git a/src/main/java/chess/controller/dto/PositionDto.java b/src/main/java/chess/controller/dto/PositionDto.java index 384a91d..32493a9 100644 --- a/src/main/java/chess/controller/dto/PositionDto.java +++ b/src/main/java/chess/controller/dto/PositionDto.java @@ -10,7 +10,7 @@ public class PositionDto { private final String name; public PositionDto(final File file, final String name) { - this.isLastFile = (file == File.h); + this.isLastFile = (file == File.H); this.name = name; } diff --git a/src/main/java/chess/domain/board/File.java b/src/main/java/chess/domain/board/File.java index 145c1dd..b55f17e 100644 --- a/src/main/java/chess/domain/board/File.java +++ b/src/main/java/chess/domain/board/File.java @@ -1,6 +1,9 @@ package chess.domain.board; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public enum File { A(1), @@ -12,21 +15,31 @@ public enum File { G(7), H(8); + private static final Map FILES = createFiles(); + private final int index; File(final int index) { this.index = index; } - public static File of(final int fileIndex) { - return Arrays.stream(File.values()) - .filter(file -> hasSameIndex(fileIndex, file)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("일치하는 파일이 존재하지 않습니다.")); + private static Map createFiles() { + HashMap files = new HashMap<>(); + + Arrays.stream(values()) + .forEach(file -> files.put(file.index, file)); + + return Collections.unmodifiableMap(files); } - private static boolean hasSameIndex(final int fileIndex, final File file) { - return file.index == fileIndex; + public static File of(final int fileIndex) { + File file = FILES.get(fileIndex); + + if (file == null) { + throw new IllegalArgumentException("일치하는 파일이 존재하지 않습니다."); + } + + return file; } public int calculateGap(final File file) { diff --git a/src/main/java/chess/domain/board/Rank.java b/src/main/java/chess/domain/board/Rank.java index 9c67b1e..b1511c1 100644 --- a/src/main/java/chess/domain/board/Rank.java +++ b/src/main/java/chess/domain/board/Rank.java @@ -1,6 +1,9 @@ package chess.domain.board; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public enum Rank { R8(8), @@ -12,21 +15,31 @@ public enum Rank { R2(2), R1(1); + private static final Map RANKS = createRanks(); + private final int index; Rank(final int index) { this.index = index; } - public static Rank of(final int rankIndex) { - return Arrays.stream(Rank.values()) - .filter(rank -> rank.hasSameIndex(rankIndex)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("일치하는 랭크가 존재하지 않습니다.")); + private static Map createRanks() { + Map ranks = new HashMap<>(); + + Arrays.stream(values()) + .forEach(rank -> ranks.put(rank.index, rank)); + + return Collections.unmodifiableMap(ranks); } - private boolean hasSameIndex(final int rankIndex) { - return this.index == rankIndex; + public static Rank of(final int rankIndex) { + Rank rank = RANKS.get(rankIndex); + + if (rank == null) { + throw new IllegalArgumentException("일치하는 랭크가 존재하지 않습니다."); + } + + return rank; } public int getIndex() { diff --git a/src/main/java/chess/domain/piece/PieceResolver.java b/src/main/java/chess/domain/piece/PieceResolver.java new file mode 100644 index 0000000..d673fc1 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceResolver.java @@ -0,0 +1,72 @@ +package chess.domain.piece; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum PieceResolver { + + KING(King.class, "k", 0), + QUEEN(Queen.class, "q", 9), + ROOK(Rook.class, "r", 5), + BISHOP(Bishop.class, "b", 3), + KNIGHT(Knight.class, "n", 2.5), + PAWN(Pawn.class, "p", 1); + + private static final Map, PieceResolver> PIECE_RESOLVERS = createPieceResolvers(); + private static final int DUPLICATION_THRESHOLD = 1; + private static final double PAWN_SCORE_ON_DUPLICATION = 0.5; + + private final Class piece; + private final String name; + private final double score; + + PieceResolver(final Class piece, final String name, final double score) { + this.piece = piece; + this.name = name; + this.score = score; + } + + private static Map, PieceResolver> createPieceResolvers() { + Map, PieceResolver> pieceResolvers = new HashMap<>(); + + Arrays.stream(values()) + .forEach(pieceResolver -> pieceResolvers.put(pieceResolver.piece, pieceResolver)); + + return Collections.unmodifiableMap(pieceResolvers); + } + + public static String findNameBy(final Piece piece) { + PieceResolver pieceResolver = PieceResolver.of(piece); + + if (piece.isWhite()) { + return pieceResolver.name; + } + + return pieceResolver.name.toUpperCase(); + } + + public static PieceResolver of(final Piece piece) { + PieceResolver pieceResolver = PIECE_RESOLVERS.get(piece.getClass()); + + if (pieceResolver == null) { + throw new IllegalArgumentException("기물에 대한 일치하는 리졸버가 존재하지 않습니다."); + } + + return pieceResolver; + } + + public static double findScoreBy(final Piece piece) { + PieceResolver pieceResolver = PieceResolver.of(piece); + return pieceResolver.score; + } + + public static double sumPawnScores(final int pawnCount) { + if (pawnCount > DUPLICATION_THRESHOLD) { + return pawnCount * PAWN_SCORE_ON_DUPLICATION; + } + + return PAWN.score; + } +} diff --git a/src/main/java/chess/domain/piece/type/PieceType.java b/src/main/java/chess/domain/piece/type/PieceType.java deleted file mode 100644 index e62961b..0000000 --- a/src/main/java/chess/domain/piece/type/PieceType.java +++ /dev/null @@ -1,74 +0,0 @@ -package chess.domain.piece.type; - -import chess.domain.piece.*; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public enum PieceType { - - KING("k", 0), - QUEEN("q", 9), - ROOK("r", 5), - BISHOP("b", 3), - KNIGHT("n", 2.5), - PAWN("p", 1); - - private static final Map, PieceType> PIECE_TYPE_MAP = createPieceTypeMap(); - private static final int DUPLICATION_THRESHOLD = 1; - private static final double PAWN_SCORE_ON_DUPLICATION = 0.5; - private final String name; - private final double score; - - PieceType(final String name, final double score) { - this.name = name; - this.score = score; - } - - private static Map, PieceType> createPieceTypeMap() { - Map, PieceType> map = new HashMap<>(); - map.put(King.class, KING); - map.put(Queen.class, QUEEN); - map.put(Rook.class, ROOK); - map.put(Bishop.class, BISHOP); - map.put(Knight.class, KNIGHT); - map.put(Pawn.class, PAWN); - return Collections.unmodifiableMap(map); - } - - public static PieceType of(Piece piece) { - return PIECE_TYPE_MAP.get(piece.getClass()); - } - - public static String findNameBy(Piece piece) { - PieceType pieceType = PieceType.of(piece); - - if (piece.isWhite()) { - return pieceType.name; - } - - return pieceType.name.toUpperCase(); - } - - public static double findScoreBy(Piece piece) { - PieceType pieceType = PieceType.of(piece); - return pieceType.score; - } - - public static boolean isKing(Piece piece) { - return PieceType.of(piece) == KING; - } - - public static boolean isPawn(Piece piece) { - return PieceType.of(piece) == PAWN; - } - - public static double sumPawnScores(final int count) { - if (count > DUPLICATION_THRESHOLD) { - return count * PAWN_SCORE_ON_DUPLICATION; - } - - return PAWN.score; - } -} diff --git a/src/test/java/chess/domain/piece/type/PieceTypeTest.java b/src/test/java/chess/domain/piece/PieceResolverTest.java similarity index 63% rename from src/test/java/chess/domain/piece/type/PieceTypeTest.java rename to src/test/java/chess/domain/piece/PieceResolverTest.java index 8c1acd7..1513fdf 100644 --- a/src/test/java/chess/domain/piece/type/PieceTypeTest.java +++ b/src/test/java/chess/domain/piece/PieceResolverTest.java @@ -1,6 +1,5 @@ -package chess.domain.piece.type; +package chess.domain.piece; -import chess.domain.piece.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -11,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class PieceTypeTest { +public class PieceResolverTest { @Test @DisplayName("피스에 해당하는 피스 타입을 반환한다.") @@ -20,10 +19,10 @@ void of() { Piece piece = new Pawn(Color.WHITE); // when - PieceType pieceType = PieceType.of(piece); + PieceResolver pieceResolver = PieceResolver.of(piece); // then - assertThat(pieceType).isSameAs(PieceType.PAWN); + assertThat(pieceResolver).isSameAs(PieceResolver.PAWN); } @ParameterizedTest @@ -31,7 +30,7 @@ void of() { @DisplayName("피스가 주어지면 해당 피스와 색상에 맞는 이름을 반환한다.") void find_name_by(Piece piece, String expected) { //given, when - String name = PieceType.findNameBy(piece); + String name = PieceResolver.findNameBy(piece); //then assertThat(name).isEqualTo(expected); @@ -40,38 +39,14 @@ void find_name_by(Piece piece, String expected) { @ParameterizedTest @MethodSource("createParamsForScore") @DisplayName("피스가 주어지면 해당 피스의 점수를 반환한다.") - void get_score(Piece piece, double expected) { + void find_score_by(Piece piece, double expected) { //given, when - double score = PieceType.findScoreBy(piece); + double score = PieceResolver.findScoreBy(piece); //then assertThat(score).isEqualTo(expected); } - @Test - @DisplayName("킹인지 확인한다.") - void is_king() { - // given - Piece king = new King(Color.WHITE); - Piece queen = new Queen(Color.WHITE); - - // when, then - assertThat(PieceType.isKing(king)).isTrue(); - assertThat(PieceType.isKing(queen)).isFalse(); - } - - @Test - @DisplayName("폰인지 확인한다.") - void is_pawn() { - // given - Piece pawn = new Pawn(Color.WHITE); - Piece queen = new Queen(Color.WHITE); - - // when, then - assertThat(PieceType.isPawn(pawn)).isTrue(); - assertThat(PieceType.isPawn(queen)).isFalse(); - } - private static Stream createParamsForName() { return Stream.of( Arguments.of(new Pawn(Color.WHITE), "p"), From 4ef547b0d04a2c4b85509275b5d11c19581b7804 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 2 Sep 2021 22:35:48 +0900 Subject: [PATCH 30/42] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94=20=EC=88=98=EC=A4=80=20=ED=95=9C=20=EB=8B=A8?= =?UTF-8?q?=EA=B3=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/piece/PieceFactory.java | 39 ++++++++----------- .../chess/domain/piece/PieceFactoryTest.java | 24 ++++++++++++ 2 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 src/test/java/chess/domain/piece/PieceFactoryTest.java diff --git a/src/main/java/chess/domain/piece/PieceFactory.java b/src/main/java/chess/domain/piece/PieceFactory.java index 0ee167e..ec051cc 100644 --- a/src/main/java/chess/domain/piece/PieceFactory.java +++ b/src/main/java/chess/domain/piece/PieceFactory.java @@ -18,39 +18,32 @@ private PieceFactory() { public static Map createPieces(final Color color) { if (color.isWhite()) { - return whitePieces(); + return initializePieces(Rank.R1, Rank.R2, WHITE); } - return blackPieces(); - } - - private static Map whitePieces() { - return initializePieces(Rank.R1, Rank.R2, WHITE); - } - private static Map blackPieces() { return initializePieces(Rank.R8, Rank.R7, BLACK); } private static Map initializePieces(final Rank rank, final Rank pawnRank, final Color color) { - Map board = new HashMap<>(); - initializePiecesExceptForPawns(rank, color, board); - initializePawns(pawnRank, color, board); - return board; + Map pieces = new HashMap<>(); + initializePiecesExceptForPawns(rank, color, pieces); + initializePawns(pawnRank, color, pieces); + return pieces; } - private static void initializePiecesExceptForPawns(final Rank rank, final Color color, final Map board) { - board.put(Position.from(File.a, rank), new Rook(color)); - board.put(Position.from(File.b, rank), new Knight(color)); - board.put(Position.from(File.c, rank), new Bishop(color)); - board.put(Position.from(File.d, rank), new Queen(color)); - board.put(Position.from(File.e, rank), new King(color)); - board.put(Position.from(File.f, rank), new Bishop(color)); - board.put(Position.from(File.g, rank), new Knight(color)); - board.put(Position.from(File.h, rank), new Rook(color)); + private static void initializePiecesExceptForPawns(final Rank rank, final Color color, final Map pieces) { + pieces.put(Position.from(File.A, rank), new Rook(color)); + pieces.put(Position.from(File.B, rank), new Knight(color)); + pieces.put(Position.from(File.C, rank), new Bishop(color)); + pieces.put(Position.from(File.D, rank), new Queen(color)); + pieces.put(Position.from(File.E, rank), new King(color)); + pieces.put(Position.from(File.F, rank), new Bishop(color)); + pieces.put(Position.from(File.G, rank), new Knight(color)); + pieces.put(Position.from(File.H, rank), new Rook(color)); } - private static void initializePawns(final Rank pawnRank, final Color color, final Map board) { + private static void initializePawns(final Rank pawnRank, final Color color, final Map pieces) { Arrays.stream(File.values()) - .forEach(file -> board.put(Position.from(file, pawnRank), new Pawn(color))); + .forEach(file -> pieces.put(Position.from(file, pawnRank), new Pawn(color))); } } diff --git a/src/test/java/chess/domain/piece/PieceFactoryTest.java b/src/test/java/chess/domain/piece/PieceFactoryTest.java new file mode 100644 index 0000000..0dff954 --- /dev/null +++ b/src/test/java/chess/domain/piece/PieceFactoryTest.java @@ -0,0 +1,24 @@ +package chess.domain.piece; + +import chess.domain.player.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class PieceFactoryTest { + + @ParameterizedTest + @CsvSource(value = {"WHITE", "BLACK"}) + @DisplayName("색상에 맞는 기물을 생성한다.") + void createPieces(Color color) { + //when + Map pieces = PieceFactory.createPieces(color); + + //then + assertThat(pieces).hasSize(16); + } +} From 6f89fbfd6d7f28fed9bc5d3a96e05940af3b36ef Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 2 Sep 2021 22:37:27 +0900 Subject: [PATCH 31/42] =?UTF-8?q?refactor:=20MoveCoordinate=20->=20Directi?= =?UTF-8?q?on=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/player/Direction.java | 85 +++++++++++++++++++ .../chess/domain/player/MoveCoordinate.java | 72 ---------------- .../chess/domain/player/DirectionTest.java | 24 ++++++ 3 files changed, 109 insertions(+), 72 deletions(-) create mode 100644 src/main/java/chess/domain/player/Direction.java delete mode 100644 src/main/java/chess/domain/player/MoveCoordinate.java create mode 100644 src/test/java/chess/domain/player/DirectionTest.java diff --git a/src/main/java/chess/domain/player/Direction.java b/src/main/java/chess/domain/player/Direction.java new file mode 100644 index 0000000..774dc98 --- /dev/null +++ b/src/main/java/chess/domain/player/Direction.java @@ -0,0 +1,85 @@ +package chess.domain.player; + +public enum Direction { + NORTH(0, 1), + SOUTH(0, -1), + EAST(1, 0), + WEST(-1, 0), + NORTH_EAST(1, 1), + SOUTH_EAST(1, -1), + NORTH_WEST(-1, 1), + SOUTH_WEST(-1, -1), + + NORTH_EAST_NORTH(1, 2), + NORTH_EAST_EAST(2, 1), + NORTH_WEST_NORTH(-1, 2), + NORTH_WEST_WEST(-2, 1), + SOUTH_EAST_SOUTH(1, -2), + SOUTH_EAST_EAST(2, -1), + SOUTH_WEST_SOUTH(-1, -2), + SOUTH_WEST_WEST(-2, -1); + + private static final int DIVISIBLE_STANDARD = 0; + private static final int CARDINAL_STANDARD = 0; + private static final int SIGN_STANDARD = 0; + + private final int x; + private final int y; + + Direction(final int x, final int y) { + this.x = x; + this.y = y; + } + + public boolean matches(final int x, final int y) { + if (isNorthOrSouth()) { + return this.x == x && isYDivisible(y) && hasSameSign(x, y); + } + + if (isEastOrWest()) { + return this.y == y && isXDivisible(x) && hasSameSign(x, y); + } + + return isMultiple(x, y) && hasSameSign(x, y); + } + + private boolean isNorthOrSouth() { + return this.x == CARDINAL_STANDARD; + } + + private boolean isEastOrWest() { + return this.y == CARDINAL_STANDARD; + } + + private boolean isYDivisible(final int y) { + return (y % this.y) == DIVISIBLE_STANDARD; + } + + private boolean isXDivisible(final int x) { + return (x % this.x) == DIVISIBLE_STANDARD; + } + + private boolean isMultiple(final int x, final int y) { + return hasSameRatio(x, y) && isDivisible(x, y); + } + + private boolean hasSameRatio(final int x, final int y) { + return (x / this.x) == (y / this.y); + } + + private boolean isDivisible(final int x, final int y) { + return isXDivisible(x) && isYDivisible(y); + } + + private boolean hasSameSign(final int x, final int y) { + return (this.x ^ x) >= SIGN_STANDARD && (this.y ^ y) >= SIGN_STANDARD; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } +} diff --git a/src/main/java/chess/domain/player/MoveCoordinate.java b/src/main/java/chess/domain/player/MoveCoordinate.java deleted file mode 100644 index 59545f0..0000000 --- a/src/main/java/chess/domain/player/MoveCoordinate.java +++ /dev/null @@ -1,72 +0,0 @@ -package chess.domain.player; - -public enum MoveCoordinate { - NORTH_EAST(1, 1), - SOUTH_EAST(1, -1), - NORTH_WEST(-1, 1), - SOUTH_WEST(-1, -1), - NORTH(0, 1), - SOUTH(0, -1), - EAST(1, 0), - WEST(-1, 0), - - WHITE_PAWN_INITIAL_NORTH(0, 2), - BLACK_PAWN_INITIAL_SOUTH(0, -2), - - NORTH_EAST_LEFT(1, 2), - NORTH_EAST_RIGHT(2, 1), - NORTH_WEST_LEFT(-2, 1), - NORTH_WEST_RIGHT(-1, 2), - SOUTH_EAST_LEFT(2, -1), - SOUTH_EAST_RIGHT(1, -2), - SOUTH_WEST_LEFT(-1, -2), - SOUTH_WEST_RIGHT(-2, -1); - - private final int file; - private final int rank; - - MoveCoordinate(final int file, final int rank) { - this.file = file; - this.rank = rank; - } - - public boolean matches(final int fileGap, final int rankGap, boolean isFinite) { - if (isFinite) { - return this.file == fileGap && this.rank == rankGap; - } - - if (this.file == 0) { - return this.file == fileGap && (rankGap % this.rank == 0) && hasSameSign(fileGap, rankGap); - } - - if (this.rank == 0) { - return this.rank == rankGap && (fileGap % this.file == 0) && hasSameSign(fileGap, rankGap); - } - - return isMultiple(fileGap, rankGap) && hasSameSign(fileGap, rankGap); - } - - private boolean isMultiple(final int fileGap, final int rankGap) { - return hasSameRate(fileGap, rankGap) && isDivisible(fileGap, rankGap); - } - - private boolean isDivisible(final int fileGap, final int rankGap) { - return (fileGap % this.file == 0) && (rankGap % this.rank == 0); - } - - private boolean hasSameRate(final int fileGap, final int rankGap) { - return (fileGap / this.file) == (rankGap / this.rank); - } - - private boolean hasSameSign(final int fileGap, final int rankGap) { - return (file ^ fileGap) >= 0 && (rank ^ rankGap) >= 0; - } - - public int getFile() { - return file; - } - - public int getRank() { - return rank; - } -} diff --git a/src/test/java/chess/domain/player/DirectionTest.java b/src/test/java/chess/domain/player/DirectionTest.java new file mode 100644 index 0000000..0e3a81e --- /dev/null +++ b/src/test/java/chess/domain/player/DirectionTest.java @@ -0,0 +1,24 @@ +package chess.domain.player; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class DirectionTest { + + @ParameterizedTest + @CsvSource(value = {"0, 2, NORTH", "0, -2, SOUTH", "2, 0, EAST", "-2, 0, WEST", + "2, 2, NORTH_EAST", "2, -2, SOUTH_EAST", "-2, 2, NORTH_WEST", "-2, -2, SOUTH_WEST", + "2, 4, NORTH_EAST_NORTH", "4, 2, NORTH_EAST_EAST", "-2, 4, NORTH_WEST_NORTH", "-4, 2, NORTH_WEST_WEST", + "2, -4, SOUTH_EAST_SOUTH", "4, -2, SOUTH_EAST_EAST", "-2, -4, SOUTH_WEST_SOUTH", "-4, -2, SOUTH_WEST_WEST"}) + @DisplayName("주어진 좌표로 이동할 수 있는 방향이 존재하는지 확인한다.") + void matches(int x, int y, Direction direction) { + //when + boolean isMatched = direction.matches(x, y); + + //then + assertThat(isMatched).isTrue(); + } +} From f90cb51afbcebafe8dabc1cff87b9e55d9f165f1 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Thu, 2 Sep 2021 22:40:12 +0900 Subject: [PATCH 32/42] =?UTF-8?q?refactor:=20MovePattern=20ENUM=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/piece/MovePattern.java | 67 +++++++++++ .../chess/domain/piece/type/MovePattern.java | 112 ------------------ .../chess/domain/piece/MovePatternTest.java | 48 ++++++++ .../domain/piece/type/MovePatternTest.java | 44 ------- 4 files changed, 115 insertions(+), 156 deletions(-) create mode 100644 src/main/java/chess/domain/piece/MovePattern.java delete mode 100644 src/main/java/chess/domain/piece/type/MovePattern.java create mode 100644 src/test/java/chess/domain/piece/MovePatternTest.java delete mode 100644 src/test/java/chess/domain/piece/type/MovePatternTest.java diff --git a/src/main/java/chess/domain/piece/MovePattern.java b/src/main/java/chess/domain/piece/MovePattern.java new file mode 100644 index 0000000..ed680f7 --- /dev/null +++ b/src/main/java/chess/domain/piece/MovePattern.java @@ -0,0 +1,67 @@ +package chess.domain.piece; + +import chess.domain.player.Direction; + +import java.util.Arrays; +import java.util.List; + +import static chess.domain.player.Direction.EAST; +import static chess.domain.player.Direction.NORTH; +import static chess.domain.player.Direction.NORTH_EAST; +import static chess.domain.player.Direction.NORTH_EAST_EAST; +import static chess.domain.player.Direction.NORTH_EAST_NORTH; +import static chess.domain.player.Direction.NORTH_WEST; +import static chess.domain.player.Direction.NORTH_WEST_NORTH; +import static chess.domain.player.Direction.NORTH_WEST_WEST; +import static chess.domain.player.Direction.SOUTH; +import static chess.domain.player.Direction.SOUTH_EAST; +import static chess.domain.player.Direction.SOUTH_EAST_EAST; +import static chess.domain.player.Direction.SOUTH_EAST_SOUTH; +import static chess.domain.player.Direction.SOUTH_WEST; +import static chess.domain.player.Direction.SOUTH_WEST_SOUTH; +import static chess.domain.player.Direction.SOUTH_WEST_WEST; +import static chess.domain.player.Direction.WEST; + +public enum MovePattern { + CARDINAL_COORDINATES(Arrays.asList(NORTH, SOUTH, WEST, EAST)), + + DIAGONAL_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), + + ALL_COORDINATES(Arrays.asList(NORTH, SOUTH, WEST, EAST, NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), + + WHITE_PAWN_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST, NORTH)), + + WHITE_PAWN_ATTACK_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST)), + + BLACK_PAWN_COORDINATES(Arrays.asList(SOUTH_EAST, SOUTH_WEST, SOUTH)), + + BLACK_PAWN_ATTACK_COORDINATES(Arrays.asList(SOUTH_EAST, SOUTH_WEST)), + + KNIGHT_COORDINATES(Arrays.asList(NORTH_EAST_NORTH, NORTH_EAST_EAST, NORTH_WEST_WEST, NORTH_WEST_NORTH, + SOUTH_EAST_EAST, SOUTH_EAST_SOUTH, SOUTH_WEST_SOUTH, SOUTH_WEST_WEST)); + + private final List coordinates; + + MovePattern(final List coordinates) { + this.coordinates = coordinates; + } + + public static MovePattern findPawnMovePattern(final Color color) { + if (color.isWhite()) { + return WHITE_PAWN_COORDINATES; + } + + return BLACK_PAWN_COORDINATES; + } + + public Direction findDirection(int fileGap, int rankGap) { + return this.coordinates.stream() + .filter(direction -> direction.matches(fileGap, rankGap)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("매칭되는 방향이 없습니다.")); + } + + public List getCoordinates() { + return coordinates; + } +} diff --git a/src/main/java/chess/domain/piece/type/MovePattern.java b/src/main/java/chess/domain/piece/type/MovePattern.java deleted file mode 100644 index 9c926da..0000000 --- a/src/main/java/chess/domain/piece/type/MovePattern.java +++ /dev/null @@ -1,112 +0,0 @@ -package chess.domain.piece.type; - -import chess.domain.piece.Color; -import chess.domain.player.MoveCoordinate; - -import java.util.*; - -import static chess.domain.player.MoveCoordinate.*; - -public class MovePattern { - - private static final Collection CARDINAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( - NORTH, SOUTH, WEST, EAST - )); - - private static final Collection DIAGONAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( - NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST - )); - - private static final Collection WHITE_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( - WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH - )); - - private static final Collection WHITE_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( - NORTH_EAST, NORTH_WEST - )); - - private static final Collection BLACK_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( - BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH - )); - - private static final Collection BLACK_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( - SOUTH_EAST, SOUTH_WEST - )); - - private static final Collection KNIGHT_COORDINATES = Collections.unmodifiableList(Arrays.asList( - NORTH_EAST_LEFT, NORTH_EAST_RIGHT, NORTH_WEST_LEFT, NORTH_WEST_RIGHT, - SOUTH_EAST_LEFT, SOUTH_EAST_RIGHT, SOUTH_WEST_LEFT, SOUTH_WEST_RIGHT - )); - - private final Collection infiniteMoveCoordinates; - private final Collection finiteMoveCoordinates; - - private MovePattern(final Collection infiniteMoveCoordinates, final Collection finiteMoveCoordinates) { - this.infiniteMoveCoordinates = Collections.unmodifiableCollection(infiniteMoveCoordinates); - this.finiteMoveCoordinates = Collections.unmodifiableCollection(finiteMoveCoordinates); - } - - public static MovePattern queenPattern() { - return new MovePattern(DIAGONAL_COORDINATES, CARDINAL_COORDINATES); - } - - public static MovePattern kingPattern() { - List finiteMoveCoordinates = new ArrayList<>(CARDINAL_COORDINATES); - finiteMoveCoordinates.addAll(DIAGONAL_COORDINATES); - return new MovePattern(Collections.emptyList(), finiteMoveCoordinates); - } - - public static MovePattern knightPattern() { - return new MovePattern(Collections.emptyList(), KNIGHT_COORDINATES); - } - - public static MovePattern rookPattern() { - return new MovePattern(CARDINAL_COORDINATES, Collections.emptyList()); - } - - public static MovePattern bishopPattern() { - return new MovePattern(DIAGONAL_COORDINATES, Collections.emptyList()); - } - - public static MovePattern pawnPattern(final Color color) { - if (color.isWhite()) { - return new MovePattern(Collections.emptyList(), WHITE_PAWN_COORDINATES); - } - return new MovePattern(Collections.emptyList(), BLACK_PAWN_COORDINATES); - } - - public MoveCoordinate findMoveCoordinate(int fileGap, int rankGap) { - List result = new ArrayList<>(); - - infiniteMoveCoordinates.stream() - .filter(moveCoordinate -> moveCoordinate.matches(fileGap, rankGap, false)) - .findAny() - .ifPresent(result::add); - - finiteMoveCoordinates.stream() - .filter(moveCoordinate -> moveCoordinate.matches(fileGap, rankGap, true)) - .findAny() - .ifPresent(result::add); - - if (result.isEmpty()) { - throw new IllegalArgumentException("매칭되는 방향이 없습니다."); - } - - return result.get(0); - } - - public Collection finiteMoveCoordinates() { - return finiteMoveCoordinates; - } - - public Collection infiniteMoveCoordinates() { - return infiniteMoveCoordinates; - } - - public Collection pawnAttackMoveCoordinates(boolean isWhite) { - if (isWhite) { - return WHITE_PAWN_ATTACK_COORDINATES; - } - return BLACK_PAWN_ATTACK_COORDINATES; - } -} diff --git a/src/test/java/chess/domain/piece/MovePatternTest.java b/src/test/java/chess/domain/piece/MovePatternTest.java new file mode 100644 index 0000000..cc7230a --- /dev/null +++ b/src/test/java/chess/domain/piece/MovePatternTest.java @@ -0,0 +1,48 @@ +package chess.domain.piece; + +import chess.domain.player.Direction; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static chess.domain.player.Direction.NORTH; +import static chess.domain.player.Direction.NORTH_EAST; +import static chess.domain.player.Direction.NORTH_WEST; +import static chess.domain.player.Direction.SOUTH; +import static chess.domain.player.Direction.SOUTH_EAST; +import static chess.domain.player.Direction.SOUTH_WEST; +import static org.assertj.core.api.Assertions.assertThat; + +class MovePatternTest { + + @Test + @DisplayName("색상에 따라 폰 패턴을 반환한다.") + void pawn_pattern_black() { + // given + Color color = Color.BLACK; + MovePattern movePattern = MovePattern.findPawnMovePattern(color); + + // when + Collection directions = movePattern.getCoordinates(); + + // then + assertThat(directions) + .containsExactlyInAnyOrder(SOUTH_EAST, SOUTH_WEST, SOUTH); + } + + @Test + @DisplayName("색상에 따라 폰 패턴을 반환한다.") + void pawn_pattern_white() { + // given + Color color = Color.WHITE; + MovePattern movePattern = MovePattern.findPawnMovePattern(color); + + // when + Collection directions = movePattern.getCoordinates(); + + // then + assertThat(directions) + .containsExactlyInAnyOrder(NORTH_EAST, NORTH_WEST, NORTH); + } +} diff --git a/src/test/java/chess/domain/piece/type/MovePatternTest.java b/src/test/java/chess/domain/piece/type/MovePatternTest.java deleted file mode 100644 index a45bb0e..0000000 --- a/src/test/java/chess/domain/piece/type/MovePatternTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package chess.domain.piece.type; - -import chess.domain.piece.Color; -import chess.domain.player.MoveCoordinate; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collection; - -import static chess.domain.player.MoveCoordinate.*; -import static org.assertj.core.api.Assertions.assertThat; - -class MovePatternTest { - - @Test - @DisplayName("색상에 따라 폰 패턴을 반환한다.") - void pawn_pattern_black() { - // given - Color color = Color.BLACK; - - // when - MovePattern movePattern = MovePattern.pawnPattern(color); - Collection moveCoordinates = movePattern.finiteMoveCoordinates(); - - // then - assertThat(moveCoordinates) - .containsExactlyInAnyOrder(BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH); - } - - @Test - @DisplayName("색상에 따라 폰 패턴을 반환한다.") - void pawn_pattern_white() { - // given - Color color = Color.WHITE; - - // when - MovePattern movePattern = MovePattern.pawnPattern(color); - Collection moveCoordinates = movePattern.finiteMoveCoordinates(); - - // then - assertThat(moveCoordinates) - .containsExactlyInAnyOrder(WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH); - } -} From 93b8d34238db7ec11a557218dba3e61b435dde48 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 3 Sep 2021 00:56:12 +0900 Subject: [PATCH 33/42] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=AC=BC=20=EB=8B=A8=EC=9C=84=20=EB=B0=A9=ED=96=A5?= =?UTF-8?q?=EB=A7=8C=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/piece/Bishop.java | 4 +- src/main/java/chess/domain/piece/King.java | 39 ++++++++++- src/main/java/chess/domain/piece/Knight.java | 4 +- src/main/java/chess/domain/piece/Pawn.java | 64 ++++++++++--------- src/main/java/chess/domain/piece/Piece.java | 33 +++++----- src/main/java/chess/domain/piece/Queen.java | 4 +- src/main/java/chess/domain/piece/Rook.java | 4 +- .../java/chess/domain/piece/BishopTest.java | 8 +-- .../java/chess/domain/piece/KingTest.java | 15 ++++- .../java/chess/domain/piece/PawnTest.java | 12 ++++ .../java/chess/domain/piece/QueenTest.java | 33 +++++++--- .../java/chess/domain/piece/RookTest.java | 8 +-- 12 files changed, 151 insertions(+), 77 deletions(-) diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index e6a7a6a..14d6051 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -1,10 +1,8 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; - public class Bishop extends Piece { public Bishop(final Color color) { - super(MovePattern.bishopPattern(), color); + super(MovePattern.DIAGONAL_COORDINATES, color, true); } } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index f3c7b73..01ba652 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -1,10 +1,45 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; +import chess.domain.player.Direction; +import chess.domain.player.Position; + +import java.util.Set; + +import static java.lang.Math.abs; public class King extends Piece { + private static final int MAX_THRESHOLD = 2; + public King(final Color color) { - super(MovePattern.kingPattern(), color); + super(MovePattern.ALL_COORDINATES, color, false); + } + + @Override + public Set findPath(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + int absoluteFileGap = abs(fileGap); + int absoluteRankGap = abs(rankGap); + + validateThreshold(absoluteFileGap, absoluteRankGap); + + Direction direction = movePattern.findDirection(fileGap, rankGap); + return source.findPassingPositions(target, direction); + } + + private void validateThreshold(final int absoluteFileGap, final int absoluteRankGap) { + if (isGreaterOrEqualThan(absoluteFileGap) || isGreaterOrEqualThan(absoluteRankGap)) { + throw new IllegalArgumentException(); + } + } + + private boolean isGreaterOrEqualThan(final int fileGap) { + return fileGap >= MAX_THRESHOLD; + } + + @Override + public boolean isKing() { + return true; } } diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index 14e65ee..c10b155 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -1,10 +1,8 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; - public class Knight extends Piece { public Knight(final Color color) { - super(MovePattern.knightPattern(), color); + super(MovePattern.KNIGHT_COORDINATES, color, false); } } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index 0d23f38..c45a54e 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -1,73 +1,79 @@ package chess.domain.piece; import chess.domain.board.Rank; -import chess.domain.piece.type.MovePattern; -import chess.domain.player.MoveCoordinate; +import chess.domain.player.Direction; import chess.domain.player.Position; import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; -import static chess.domain.player.MoveCoordinate.*; +import static java.lang.Math.abs; public class Pawn extends Piece { private static final Rank WHITE_INITIAL_RANK = Rank.R2; private static final Rank BLACK_INITIAL_RANK = Rank.R7; + private static final int MAX_RANK_THRESHOLD = 2; public Pawn(final Color color) { - super(MovePattern.pawnPattern(color), color); + super(MovePattern.findPawnMovePattern(color), color, false); } @Override public Set findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); + boolean initialMove = isInitialMove(source); + int absoluteRankGap = abs(rankGap); - MoveCoordinate moveCoordinate = movePattern.findMoveCoordinate(fileGap, rankGap); - moveCoordinate = resetIfInitialMove(moveCoordinate, isInitialMove(source)); + validateInitialMoveThreshold(absoluteRankGap, initialMove); + validateFollowingMoveThreshold(absoluteRankGap, initialMove); - return source.findPassingPositions(target, moveCoordinate); + Direction direction = movePattern.findDirection(fileGap, rankGap); + return source.findPassingPositions(target, direction); } - private boolean isInitialMove(Position source) { + private boolean isInitialMove(final Position source) { return source.hasSameRank(WHITE_INITIAL_RANK) || source.hasSameRank(BLACK_INITIAL_RANK); } - private MoveCoordinate resetIfInitialMove(MoveCoordinate moveCoordinate, boolean isInitialMove) { - validateMoveCount(moveCoordinate, isInitialMove); - - if (isWhiteInitialMove(moveCoordinate)) { - return NORTH; - } - - if (isBlackInitialMove(moveCoordinate)) { - return SOUTH; + private void validateInitialMoveThreshold(final int absoluteRankGap, final boolean isInitialMove) { + if (isGreaterThanMaxRankThreshold(absoluteRankGap) && isInitialMove) { + throw new IllegalArgumentException("최초 이동 시 최대 2칸까지 이동할 수 있습니다."); } - - return moveCoordinate; } - private void validateMoveCount(MoveCoordinate moveCoordinate, boolean isInitialMove) { - if (!isInitialMove && (isBlackInitialMove(moveCoordinate) || isWhiteInitialMove(moveCoordinate))) { - throw new IllegalArgumentException("최초 이동 시에만 2칸 이동할 수 있습니다."); + private void validateFollowingMoveThreshold(final int absoluteRankGap, final boolean isInitialMove) { + if ((isGreaterOrEqualToMaxThreshold(absoluteRankGap) && !isInitialMove)) { + throw new IllegalArgumentException("최초 이동이 아닐 시 1칸만 이동할 수 있습니다."); } } - private boolean isWhiteInitialMove(final MoveCoordinate moveCoordinate) { - return moveCoordinate == WHITE_PAWN_INITIAL_NORTH; + private boolean isGreaterThanMaxRankThreshold(final int absoluteRankGap) { + return absoluteRankGap > MAX_RANK_THRESHOLD; } - private boolean isBlackInitialMove(final MoveCoordinate moveCoordinate) { - return moveCoordinate == BLACK_PAWN_INITIAL_SOUTH; + private boolean isGreaterOrEqualToMaxThreshold(final int absoluteRankGap) { + return absoluteRankGap >= MAX_RANK_THRESHOLD; } @Override - public Collection findAvailableAttackPositions(final Position position) { - return movePattern.pawnAttackMoveCoordinates(isWhite()).stream() - .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, true)) + public Set findAvailableAttackPositions(final Position position) { + return movePattern.getCoordinates().stream() + .filter(direction -> abs(direction.getX()) > 0) + .map(direction -> position.findAvailablePositions(direction, false)) .flatMap(Collection::stream) .collect(Collectors.toSet()); } + + @Override + public boolean isPawn() { + return true; + } + + @Override + public boolean isNotPawn() { + return false; + } } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 1f72110..34c6738 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -1,11 +1,9 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; -import chess.domain.player.MoveCoordinate; +import chess.domain.player.Direction; import chess.domain.player.Position; import java.util.Collection; -import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -13,10 +11,12 @@ public abstract class Piece { protected final MovePattern movePattern; private final Color color; + private final boolean canMoveInfinitely; - Piece(final MovePattern movePattern, final Color color) { + Piece(final MovePattern movePattern, final Color color, final boolean canMoveInfinitely) { this.movePattern = movePattern; this.color = color; + this.canMoveInfinitely = canMoveInfinitely; } public boolean isWhite() { @@ -27,24 +27,27 @@ public Set findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); - MoveCoordinate moveCoordinate = movePattern.findMoveCoordinate(fileGap, rankGap); - return source.findPassingPositions(target, moveCoordinate); + Direction direction = movePattern.findDirection(fileGap, rankGap); + return source.findPassingPositions(target, direction); } public Collection findAvailableAttackPositions(final Position position) { - Set finitePositions = movePattern.finiteMoveCoordinates().stream() - .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, true)) + return movePattern.getCoordinates().stream() + .map(direction -> position.findAvailablePositions(direction, canMoveInfinitely)) .flatMap(Collection::stream) .collect(Collectors.toSet()); + } - Set infinitePositions = movePattern.infiniteMoveCoordinates().stream() - .map(moveCoordinate -> position.findAvailablePositions(moveCoordinate, false)) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); + public boolean isKing() { + return false; + } + + public boolean isPawn() { + return false; + } - Collection positions = new HashSet<>(finitePositions); - positions.addAll(infinitePositions); - return positions; + public boolean isNotPawn() { + return true; } } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index 9c68242..e09081a 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -1,10 +1,8 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; - public class Queen extends Piece { public Queen(final Color color) { - super(MovePattern.queenPattern(), color); + super(MovePattern.ALL_COORDINATES, color, true); } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index 2a7bba0..1fac29d 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -1,10 +1,8 @@ package chess.domain.piece; -import chess.domain.piece.type.MovePattern; - public class Rook extends Piece { public Rook(final Color color) { - super(MovePattern.rookPattern(), color); + super(MovePattern.CARDINAL_COORDINATES, color, true); } } diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index 64eb8b5..2165239 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -74,10 +74,10 @@ void find_available_attack_positions() { private static Stream createParameters() { return Stream.of( - Arguments.of("b6", tuple(File.c, Rank.R5)), - Arguments.of("b2", tuple(File.c, Rank.R3)), - Arguments.of("f2", tuple(File.e, Rank.R3)), - Arguments.of("f6", tuple(File.e, Rank.R5)) + Arguments.of("b6", tuple(File.C, Rank.R5)), + Arguments.of("b2", tuple(File.C, Rank.R3)), + Arguments.of("f2", tuple(File.E, Rank.R3)), + Arguments.of("f6", tuple(File.E, Rank.R5)) ); } } diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java index 2dc762e..73c0228 100644 --- a/src/test/java/chess/domain/piece/KingTest.java +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -32,7 +32,8 @@ void find_paths_success(String targetPosition) { } @ParameterizedTest - @ValueSource(strings = {"c2", "d2", "e2", "f2", "b3", "b4", "b5"}) + @ValueSource(strings = {"b2", "b3", "b4", "b5", "b6", "c6", "d6", "e6", + "f6", "f5", "f4", "f3", "f2", "c2", "d2", "e2"}) @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") void find_paths_invalid_target(String invalidTarget) { //given @@ -65,4 +66,16 @@ void find_available_attack_positions() { .hasSize(expected.size()) .containsAll(expected); } + + @Test + @DisplayName("킹인지 확인한다.") + void is_king() { + // given + Piece king = new King(Color.WHITE); + Piece queen = new Queen(Color.WHITE); + + // when, then + assertThat(king.isKing()).isTrue(); + assertThat(queen.isKing()).isFalse(); + } } diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java index 3d702ed..e3a7cf0 100644 --- a/src/test/java/chess/domain/piece/PawnTest.java +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -124,4 +124,16 @@ void find_available_attack_positions() { .hasSize(expected.size()) .containsAll(expected); } + + @Test + @DisplayName("폰인지 확인한다.") + void is_pawn() { + // given + Piece pawn = new Pawn(Color.WHITE); + Piece queen = new Queen(Color.WHITE); + + // when, then + assertThat(pawn.isPawn()).isTrue(); + assertThat(queen.isPawn()).isFalse(); + } } diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java index 67b1ca9..e3b4eda 100644 --- a/src/test/java/chess/domain/piece/QueenTest.java +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -40,9 +40,9 @@ void find_paths_success_diagonal(String targetPosition, Tuple expected) { } @ParameterizedTest - @ValueSource(strings = {"d5", "c4", "e4", "d3"}) + @MethodSource("createParametersForCardinal") @DisplayName("출발과 도착 위치가 주어지면 지나가는 경로를 반환한다.") - void find_paths_success_cardinal(String targetPosition) { + void find_paths_success_cardinal(String targetPosition, Tuple expected) { //given Position source = Position.of("d4"); Position target = Position.of(targetPosition); @@ -52,11 +52,14 @@ void find_paths_success_cardinal(String targetPosition) { Set paths = piece.findPath(source, target); //then - assertThat(paths).isEmpty(); + //then + assertThat(paths).extracting("file", "rank") + .containsOnly(expected); } @ParameterizedTest - @ValueSource(strings = {"d6", "d2", "b4", "f4", "f5"}) + @ValueSource(strings = {"c2", "e2", "c6", "e6", + "b3", "b5", "f3", "f5"}) @DisplayName("도착 위치가 이동할 수 없는 경로일 경우 예외가 발생한다.") void find_paths_invalid_target(String invalidTarget) { //given @@ -76,9 +79,10 @@ void find_available_attack_positions() { Position position = Position.of("d4"); Piece queen = new Queen(Color.WHITE); Collection expected = Arrays.asList( + Position.of("a4"), Position.of("b4"), Position.of("c4"), Position.of("e4"), Position.of("f4"), Position.of("g4"), Position.of("h4"), + Position.of("d1"), Position.of("d2"), Position.of("d3"), Position.of("d5"), Position.of("d6"), Position.of("d7"), Position.of("d8"), Position.of("a1"), Position.of("b2"), Position.of("c3"), Position.of("e5"), Position.of("f6"), Position.of("g7"), Position.of("h8"), - Position.of("a7"), Position.of("b6"), Position.of("c5"), Position.of("e3"), Position.of("f2"), Position.of("g1"), - Position.of("d3"), Position.of("d5"), Position.of("c4"), Position.of("e4") + Position.of("a7"), Position.of("b6"), Position.of("c5"), Position.of("e3"), Position.of("f2"), Position.of("g1") ); //when @@ -92,10 +96,19 @@ void find_available_attack_positions() { private static Stream createParametersForDiagonal() { return Stream.of( - Arguments.of("b6", tuple(File.c, Rank.R5)), - Arguments.of("b2", tuple(File.c, Rank.R3)), - Arguments.of("f2", tuple(File.e, Rank.R3)), - Arguments.of("f6", tuple(File.e, Rank.R5)) + Arguments.of("b6", tuple(File.C, Rank.R5)), + Arguments.of("b2", tuple(File.C, Rank.R3)), + Arguments.of("f2", tuple(File.E, Rank.R3)), + Arguments.of("f6", tuple(File.E, Rank.R5)) + ); + } + + private static Stream createParametersForCardinal() { + return Stream.of( + Arguments.of("d6", tuple(File.D, Rank.R5)), + Arguments.of("d2", tuple(File.D, Rank.R3)), + Arguments.of("b4", tuple(File.C, Rank.R4)), + Arguments.of("f4", tuple(File.E, Rank.R4)) ); } } diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java index 4331349..94e38e1 100644 --- a/src/test/java/chess/domain/piece/RookTest.java +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -73,10 +73,10 @@ void find_available_attack_positions() { private static Stream createParameters() { return Stream.of( - Arguments.of("d2", tuple(File.d, Rank.R3)), - Arguments.of("d6", tuple(File.d, Rank.R5)), - Arguments.of("b4", tuple(File.c, Rank.R4)), - Arguments.of("f4", tuple(File.e, Rank.R4)) + Arguments.of("d2", tuple(File.D, Rank.R3)), + Arguments.of("d6", tuple(File.D, Rank.R5)), + Arguments.of("b4", tuple(File.C, Rank.R4)), + Arguments.of("f4", tuple(File.E, Rank.R4)) ); } } From 8b1d2f53b68bdf4bc0247746a19911de6a604cf3 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 3 Sep 2021 00:58:12 +0900 Subject: [PATCH 34/42] =?UTF-8?q?refactor:=20AttackPositions=20->=20Attack?= =?UTF-8?q?Range=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{AttackPositions.java => AttackRange.java} | 43 ++++++++----------- ...ositionsTest.java => AttackRangeTest.java} | 22 +++++----- 2 files changed, 30 insertions(+), 35 deletions(-) rename src/main/java/chess/domain/player/{AttackPositions.java => AttackRange.java} (60%) rename src/test/java/chess/domain/player/{AttackPositionsTest.java => AttackRangeTest.java} (61%) diff --git a/src/main/java/chess/domain/player/AttackPositions.java b/src/main/java/chess/domain/player/AttackRange.java similarity index 60% rename from src/main/java/chess/domain/player/AttackPositions.java rename to src/main/java/chess/domain/player/AttackRange.java index 2007382..60855c5 100644 --- a/src/main/java/chess/domain/player/AttackPositions.java +++ b/src/main/java/chess/domain/player/AttackRange.java @@ -6,19 +6,26 @@ import java.util.HashMap; import java.util.Map; -public class AttackPositions { +public class AttackRange { private static final int EMPTY = 0; + private static final int COUNT_UNIT = 1; private final Map counts = new HashMap<>(); - public AttackPositions(final Map pieces) { - pieces.keySet() - .forEach(position -> { - Piece piece = pieces.get(position); - Collection positions = piece.findAvailableAttackPositions(position); - positions.forEach(this::increase); - }); + public AttackRange(final Map pieces) { + pieces.forEach((position, piece) -> { + Collection positions = piece.findAvailableAttackPositions(position); + positions.forEach(this::increase); + }); + } + + private void increase(final Position position) { + counts.put(position, counts.getOrDefault(position, EMPTY) + COUNT_UNIT); + } + + public boolean contains(final Position position) { + return counts.containsKey(position) && (counts.get(position) > EMPTY); } public void update(final Position source, final Position target, final Piece piece) { @@ -31,24 +38,12 @@ public void remove(final Position position, final Piece piece) { previousAttackPositions.forEach(this::decrease); } - private void add(final Position position, final Piece piece) { - Collection currentAttackPositions = piece.findAvailableAttackPositions(position); - currentAttackPositions.forEach(this::increase); - } - private void decrease(final Position position) { - counts.put(position, counts.get(position) - 1); - } - - private void increase(final Position position) { - counts.put(position, counts.getOrDefault(position, 0) + 1); - } - - public boolean isEmpty(final Position position) { - return !counts.containsKey(position) || (counts.get(position) == EMPTY); + counts.put(position, counts.get(position) - COUNT_UNIT); } - public boolean contains(final Position position) { - return counts.containsKey(position); + private void add(final Position position, final Piece piece) { + Collection currentAttackPositions = piece.findAvailableAttackPositions(position); + currentAttackPositions.forEach(this::increase); } } diff --git a/src/test/java/chess/domain/player/AttackPositionsTest.java b/src/test/java/chess/domain/player/AttackRangeTest.java similarity index 61% rename from src/test/java/chess/domain/player/AttackPositionsTest.java rename to src/test/java/chess/domain/player/AttackRangeTest.java index bc9dc11..2c9319f 100644 --- a/src/test/java/chess/domain/player/AttackPositionsTest.java +++ b/src/test/java/chess/domain/player/AttackRangeTest.java @@ -13,7 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat; -class AttackPositionsTest { +class AttackRangeTest { @ParameterizedTest @ValueSource(strings = {"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3"}) @@ -21,14 +21,14 @@ class AttackPositionsTest { void create(String key) { //given Map pieces = PieceFactory.createPieces(Color.WHITE); - AttackPositions attackPositions = new AttackPositions(pieces); + AttackRange attackRange = new AttackRange(pieces); Position position = Position.of(key); //when - boolean isEmpty = attackPositions.isEmpty(position); + boolean contains = attackRange.contains(position); //then - assertThat(isEmpty).isFalse(); + assertThat(contains).isTrue(); } @Test @@ -36,18 +36,18 @@ void create(String key) { void update() { //given Map pieces = PieceFactory.createPieces(Color.WHITE); - AttackPositions attackPositions = new AttackPositions(pieces); + AttackRange attackRange = new AttackRange(pieces); Position before = Position.of("b1"); Position current = Position.of("c3"); //when - attackPositions.update(before, current, new Knight(Color.WHITE)); + attackRange.update(before, current, new Knight(Color.WHITE)); //then - assertThat(attackPositions.isEmpty(Position.of("a3"))).isFalse(); - assertThat(attackPositions.isEmpty(Position.of("b1"))).isFalse(); - assertThat(attackPositions.isEmpty(Position.of("c3"))).isFalse(); - assertThat(attackPositions.isEmpty(Position.of("b5"))).isFalse(); - assertThat(attackPositions.isEmpty(Position.of("d5"))).isFalse(); + assertThat(attackRange.contains(Position.of("a3"))).isTrue(); + assertThat(attackRange.contains(Position.of("b1"))).isTrue(); + assertThat(attackRange.contains(Position.of("c3"))).isTrue(); + assertThat(attackRange.contains(Position.of("b5"))).isTrue(); + assertThat(attackRange.contains(Position.of("d5"))).isTrue(); } } From 123d3d34999cd52a56428295c822db5b8413584b Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 3 Sep 2021 00:58:43 +0900 Subject: [PATCH 35/42] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98,=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/chess/domain/player/Position.java | 52 +++++++++++-------- .../chess/domain/player/PositionTest.java | 22 ++++---- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/main/java/chess/domain/player/Position.java b/src/main/java/chess/domain/player/Position.java index 895b6fc..970919b 100644 --- a/src/main/java/chess/domain/player/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -39,7 +39,13 @@ private static String createKey(final File file, final Rank rank) { } public static Position of(final String key) { - return POSITIONS.get(key); + Position position = POSITIONS.get(key.toUpperCase()); + + if (position == null) { + throw new IllegalArgumentException("해당 파일과 랭크에 대한 위치가 존재하지 않습니다."); + } + + return position; } public static Position from(final File file, final Rank rank) { @@ -62,12 +68,12 @@ public int calculateRankGap(final Position position) { return rank.calculateGap(position.getRank()); } - public Set findPassingPositions(final Position target, final MoveCoordinate moveCoordinate) { + public Set findPassingPositions(final Position target, final Direction direction) { Set positions = new HashSet<>(); Position current = this; while (target.isDifferent(current)) { - current = current.move(moveCoordinate); + current = current.move(direction); positions.add(current); } @@ -79,41 +85,41 @@ private boolean isDifferent(final Position current) { return !this.equals(current); } - public Collection findAvailablePositions(final MoveCoordinate moveCoordinate, final boolean isFinite) { - if (isFinite) { - return getFinitePositions(moveCoordinate); + public Collection findAvailablePositions(final Direction direction, final boolean canMoveInfinitely) { + if (canMoveInfinitely) { + return findInfinitePositions(direction); } - return getInfinitePositions(moveCoordinate); + return findFinitePositions(direction); } - private Collection getFinitePositions(final MoveCoordinate moveCoordinate) { - if (isMovable(moveCoordinate)) { - return Collections.singleton(move(moveCoordinate)); - } - - return Collections.emptySet(); - } - - private Collection getInfinitePositions(final MoveCoordinate moveCoordinate) { + private Collection findInfinitePositions(final Direction direction) { Collection positions = new HashSet<>(); Position current = this; - while (current.isMovable(moveCoordinate)) { - current = current.move(moveCoordinate); + while (current.isMovable(direction)) { + current = current.move(direction); positions.add(current); } return positions; } - private boolean isMovable(final MoveCoordinate moveCoordinate) { - return rank.canMove(moveCoordinate.getRank()) && file.canMove(moveCoordinate.getFile()); + private Collection findFinitePositions(final Direction direction) { + if (isMovable(direction)) { + return Collections.singleton(move(direction)); + } + + return Collections.emptySet(); + } + + private boolean isMovable(final Direction direction) { + return rank.canMove(direction.getY()) && file.canMove(direction.getX()); } - private Position move(final MoveCoordinate moveCoordinate) { - File movedFile = this.file.add(moveCoordinate.getFile()); - Rank movedRank = this.rank.add(moveCoordinate.getRank()); + private Position move(final Direction direction) { + File movedFile = this.file.move(direction.getX()); + Rank movedRank = this.rank.move(direction.getY()); return Position.from(movedFile, movedRank); } diff --git a/src/test/java/chess/domain/player/PositionTest.java b/src/test/java/chess/domain/player/PositionTest.java index 8c83bbc..f95a8d8 100644 --- a/src/test/java/chess/domain/player/PositionTest.java +++ b/src/test/java/chess/domain/player/PositionTest.java @@ -22,7 +22,7 @@ class PositionTest { @DisplayName("가로, 세로 인자에 해당하는 위치를 반환한다.") void from_file_and_rank() { //given - File file = File.a; + File file = File.A; Rank rank = Rank.R1; //when @@ -37,7 +37,7 @@ void from_file_and_rank() { @DisplayName("문자열 키에 해당하는 위치를 반환한다.") void from_key() { //given - File file = File.a; + File file = File.A; Rank rank = Rank.R1; String key = file.name() + rank.getIndex(); @@ -55,10 +55,10 @@ void find_passing_positions() { // given Position source = Position.of("d4"); Position target = Position.of("d7"); - MoveCoordinate moveCoordinate = MoveCoordinate.NORTH; + Direction direction = Direction.NORTH; // when - Set positions = source.findPassingPositions(target, moveCoordinate); + Set positions = source.findPassingPositions(target, direction); // then assertThat(positions) @@ -67,15 +67,15 @@ void find_passing_positions() { } @ParameterizedTest - @MethodSource("createParams") + @MethodSource("createParametersForAvailablePositions") @DisplayName("이동 가능한 위치를 반환한다.") - void find_available_positions(boolean isFinite, Collection expected) { + void find_available_positions(boolean canMoveInfinitely, Collection expected) { // given Position position = Position.of("d4"); - MoveCoordinate moveCoordinate = MoveCoordinate.EAST; + Direction direction = Direction.EAST; // when - Collection positions = position.findAvailablePositions(moveCoordinate, isFinite); + Collection positions = position.findAvailablePositions(direction, canMoveInfinitely); // then assertThat(positions) @@ -97,10 +97,10 @@ void has_same_rank() { assertThat(hasSameRank).isTrue(); } - private static Stream createParams() { + private static Stream createParametersForAvailablePositions() { return Stream.of( - Arguments.of(false, Arrays.asList(Position.of("e4"), Position.of("f4"), Position.of("g4"), Position.of("h4"))), - Arguments.of(true, Collections.singletonList(Position.of("e4"))) + Arguments.of(true, Arrays.asList(Position.of("e4"), Position.of("f4"), Position.of("g4"), Position.of("h4"))), + Arguments.of(false, Collections.singletonList(Position.of("e4"))) ); } } From 296dd67c42add80310fbe9b75e9be50dd0e23620 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 3 Sep 2021 03:04:06 +0900 Subject: [PATCH 36/42] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EA=B5=AC=ED=95=98=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20Position=20->=20Piece=EB=A1=9C=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/piece/Bishop.java | 2 +- src/main/java/chess/domain/piece/King.java | 8 +-- src/main/java/chess/domain/piece/Knight.java | 2 +- .../java/chess/domain/piece/MovePattern.java | 32 +++++------ src/main/java/chess/domain/piece/Pawn.java | 12 ++-- src/main/java/chess/domain/piece/Piece.java | 55 +++++++++++++++++-- src/main/java/chess/domain/piece/Queen.java | 2 +- src/main/java/chess/domain/piece/Rook.java | 2 +- .../java/chess/domain/player/Direction.java | 4 ++ .../java/chess/domain/player/Position.java | 51 +---------------- .../java/chess/domain/piece/BishopTest.java | 3 +- .../java/chess/domain/piece/KingTest.java | 3 +- .../java/chess/domain/piece/KnightTest.java | 3 +- .../chess/domain/piece/MovePatternTest.java | 4 +- .../java/chess/domain/piece/PawnTest.java | 7 +-- .../java/chess/domain/piece/QueenTest.java | 5 +- .../java/chess/domain/piece/RookTest.java | 3 +- .../chess/domain/player/PositionTest.java | 50 ----------------- 18 files changed, 98 insertions(+), 150 deletions(-) diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java index 14d6051..520f8f4 100644 --- a/src/main/java/chess/domain/piece/Bishop.java +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -3,6 +3,6 @@ public class Bishop extends Piece { public Bishop(final Color color) { - super(MovePattern.DIAGONAL_COORDINATES, color, true); + super(MovePattern.DIAGONAL_DIRECTIONS, color, true); } } diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java index 01ba652..fe07987 100644 --- a/src/main/java/chess/domain/piece/King.java +++ b/src/main/java/chess/domain/piece/King.java @@ -3,7 +3,7 @@ import chess.domain.player.Direction; import chess.domain.player.Position; -import java.util.Set; +import java.util.Collection; import static java.lang.Math.abs; @@ -12,11 +12,11 @@ public class King extends Piece { private static final int MAX_THRESHOLD = 2; public King(final Color color) { - super(MovePattern.ALL_COORDINATES, color, false); + super(MovePattern.ALL_DIRECTIONS, color, false); } @Override - public Set findPath(final Position source, final Position target) { + public Collection findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); int absoluteFileGap = abs(fileGap); @@ -25,7 +25,7 @@ public Set findPath(final Position source, final Position target) { validateThreshold(absoluteFileGap, absoluteRankGap); Direction direction = movePattern.findDirection(fileGap, rankGap); - return source.findPassingPositions(target, direction); + return findPassingPositions(source, target, direction); } private void validateThreshold(final int absoluteFileGap, final int absoluteRankGap) { diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java index c10b155..c7f1f3e 100644 --- a/src/main/java/chess/domain/piece/Knight.java +++ b/src/main/java/chess/domain/piece/Knight.java @@ -3,6 +3,6 @@ public class Knight extends Piece { public Knight(final Color color) { - super(MovePattern.KNIGHT_COORDINATES, color, false); + super(MovePattern.KNIGHT_DIRECTIONS, color, false); } } diff --git a/src/main/java/chess/domain/piece/MovePattern.java b/src/main/java/chess/domain/piece/MovePattern.java index ed680f7..f9c76e2 100644 --- a/src/main/java/chess/domain/piece/MovePattern.java +++ b/src/main/java/chess/domain/piece/MovePattern.java @@ -23,45 +23,45 @@ import static chess.domain.player.Direction.WEST; public enum MovePattern { - CARDINAL_COORDINATES(Arrays.asList(NORTH, SOUTH, WEST, EAST)), + CARDINAL_DIRECTIONS(Arrays.asList(NORTH, SOUTH, WEST, EAST)), - DIAGONAL_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), + DIAGONAL_DIRECTIONS(Arrays.asList(NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), - ALL_COORDINATES(Arrays.asList(NORTH, SOUTH, WEST, EAST, NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), + ALL_DIRECTIONS(Arrays.asList(NORTH, SOUTH, WEST, EAST, NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST)), - WHITE_PAWN_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST, NORTH)), + WHITE_PAWN_DIRECTIONS(Arrays.asList(NORTH_EAST, NORTH_WEST, NORTH)), - WHITE_PAWN_ATTACK_COORDINATES(Arrays.asList(NORTH_EAST, NORTH_WEST)), + WHITE_PAWN_ATTACK_DIRECTIONS(Arrays.asList(NORTH_EAST, NORTH_WEST)), - BLACK_PAWN_COORDINATES(Arrays.asList(SOUTH_EAST, SOUTH_WEST, SOUTH)), + BLACK_PAWN_DIRECTIONS(Arrays.asList(SOUTH_EAST, SOUTH_WEST, SOUTH)), - BLACK_PAWN_ATTACK_COORDINATES(Arrays.asList(SOUTH_EAST, SOUTH_WEST)), + BLACK_PAWN_ATTACK_DIRECTIONS(Arrays.asList(SOUTH_EAST, SOUTH_WEST)), - KNIGHT_COORDINATES(Arrays.asList(NORTH_EAST_NORTH, NORTH_EAST_EAST, NORTH_WEST_WEST, NORTH_WEST_NORTH, + KNIGHT_DIRECTIONS(Arrays.asList(NORTH_EAST_NORTH, NORTH_EAST_EAST, NORTH_WEST_WEST, NORTH_WEST_NORTH, SOUTH_EAST_EAST, SOUTH_EAST_SOUTH, SOUTH_WEST_SOUTH, SOUTH_WEST_WEST)); - private final List coordinates; + private final List directions; - MovePattern(final List coordinates) { - this.coordinates = coordinates; + MovePattern(final List directions) { + this.directions = directions; } public static MovePattern findPawnMovePattern(final Color color) { if (color.isWhite()) { - return WHITE_PAWN_COORDINATES; + return WHITE_PAWN_DIRECTIONS; } - return BLACK_PAWN_COORDINATES; + return BLACK_PAWN_DIRECTIONS; } public Direction findDirection(int fileGap, int rankGap) { - return this.coordinates.stream() + return this.directions.stream() .filter(direction -> direction.matches(fileGap, rankGap)) .findAny() .orElseThrow(() -> new IllegalArgumentException("매칭되는 방향이 없습니다.")); } - public List getCoordinates() { - return coordinates; + public List getDirections() { + return directions; } } diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java index c45a54e..73262d3 100644 --- a/src/main/java/chess/domain/piece/Pawn.java +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -21,7 +21,7 @@ public Pawn(final Color color) { } @Override - public Set findPath(final Position source, final Position target) { + public Collection findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); boolean initialMove = isInitialMove(source); @@ -31,7 +31,7 @@ public Set findPath(final Position source, final Position target) { validateFollowingMoveThreshold(absoluteRankGap, initialMove); Direction direction = movePattern.findDirection(fileGap, rankGap); - return source.findPassingPositions(target, direction); + return findPassingPositions(source, target, direction); } private boolean isInitialMove(final Position source) { @@ -59,10 +59,10 @@ private boolean isGreaterOrEqualToMaxThreshold(final int absoluteRankGap) { } @Override - public Set findAvailableAttackPositions(final Position position) { - return movePattern.getCoordinates().stream() - .filter(direction -> abs(direction.getX()) > 0) - .map(direction -> position.findAvailablePositions(direction, false)) + public Set findAvailableAttackPositions(final Position source) { + return movePattern.getDirections().stream() + .filter(Direction::isDiagonal) + .map(direction -> findAvailablePositions(source, direction, false)) .flatMap(Collection::stream) .collect(Collectors.toSet()); } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index 34c6738..6e33fc2 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -4,6 +4,8 @@ import chess.domain.player.Position; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -23,21 +25,64 @@ public boolean isWhite() { return color.isWhite(); } - public Set findPath(final Position source, final Position target) { + public Collection findPath(final Position source, final Position target) { int fileGap = target.calculateFileGap(source); int rankGap = target.calculateRankGap(source); Direction direction = movePattern.findDirection(fileGap, rankGap); - return source.findPassingPositions(target, direction); + + return findPassingPositions(source, target, direction); + } + + protected Collection findPassingPositions(final Position source, final Position target, final Direction direction) { + Set positions = new HashSet<>(); + Position current = source; + + while (current.isDifferent(target)) { + current = current.move(direction); + positions.add(current); + } + + positions.remove(target); + return positions; } - public Collection findAvailableAttackPositions(final Position position) { - return movePattern.getCoordinates().stream() - .map(direction -> position.findAvailablePositions(direction, canMoveInfinitely)) + public Collection findAvailableAttackPositions(final Position source) { + return movePattern.getDirections().stream() + .map(direction -> findAvailablePositions(source, direction, canMoveInfinitely)) .flatMap(Collection::stream) .collect(Collectors.toSet()); } + protected Collection findAvailablePositions(final Position source, final Direction direction, final boolean canMoveInfinitely) { + if (canMoveInfinitely) { + return findInfinitePositions(source, direction); + } + + return findFinitePositions(source, direction); + } + + private Collection findInfinitePositions(final Position source, final Direction direction) { + Collection positions = new HashSet<>(); + Position current = source; + + while (current.isMovable(direction)) { + current = current.move(direction); + positions.add(current); + } + + return positions; + } + + private Collection findFinitePositions(final Position source, final Direction direction) { + if (source.isMovable(direction)) { + Position movedPosition = source.move(direction); + return Collections.singleton(movedPosition); + } + + return Collections.emptySet(); + } + public boolean isKing() { return false; } diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java index e09081a..813935f 100644 --- a/src/main/java/chess/domain/piece/Queen.java +++ b/src/main/java/chess/domain/piece/Queen.java @@ -3,6 +3,6 @@ public class Queen extends Piece { public Queen(final Color color) { - super(MovePattern.ALL_COORDINATES, color, true); + super(MovePattern.ALL_DIRECTIONS, color, true); } } diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java index 1fac29d..7c22d3b 100644 --- a/src/main/java/chess/domain/piece/Rook.java +++ b/src/main/java/chess/domain/piece/Rook.java @@ -3,6 +3,6 @@ public class Rook extends Piece { public Rook(final Color color) { - super(MovePattern.CARDINAL_COORDINATES, color, true); + super(MovePattern.CARDINAL_DIRECTIONS, color, true); } } diff --git a/src/main/java/chess/domain/player/Direction.java b/src/main/java/chess/domain/player/Direction.java index 774dc98..79bf503 100644 --- a/src/main/java/chess/domain/player/Direction.java +++ b/src/main/java/chess/domain/player/Direction.java @@ -31,6 +31,10 @@ public enum Direction { this.y = y; } + public boolean isDiagonal() { + return this.x != CARDINAL_STANDARD && this.y != CARDINAL_STANDARD; + } + public boolean matches(final int x, final int y) { if (isNorthOrSouth()) { return this.x == x && isYDivisible(y) && hasSameSign(x, y); diff --git a/src/main/java/chess/domain/player/Position.java b/src/main/java/chess/domain/player/Position.java index 970919b..c621f51 100644 --- a/src/main/java/chess/domain/player/Position.java +++ b/src/main/java/chess/domain/player/Position.java @@ -4,12 +4,8 @@ import chess.domain.board.Rank; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; public class Position { @@ -68,56 +64,15 @@ public int calculateRankGap(final Position position) { return rank.calculateGap(position.getRank()); } - public Set findPassingPositions(final Position target, final Direction direction) { - Set positions = new HashSet<>(); - Position current = this; - - while (target.isDifferent(current)) { - current = current.move(direction); - positions.add(current); - } - - positions.remove(target); - return positions; - } - - private boolean isDifferent(final Position current) { + public boolean isDifferent(final Position current) { return !this.equals(current); } - public Collection findAvailablePositions(final Direction direction, final boolean canMoveInfinitely) { - if (canMoveInfinitely) { - return findInfinitePositions(direction); - } - - return findFinitePositions(direction); - } - - private Collection findInfinitePositions(final Direction direction) { - Collection positions = new HashSet<>(); - Position current = this; - - while (current.isMovable(direction)) { - current = current.move(direction); - positions.add(current); - } - - return positions; - } - - private Collection findFinitePositions(final Direction direction) { - if (isMovable(direction)) { - return Collections.singleton(move(direction)); - } - - return Collections.emptySet(); - } - - private boolean isMovable(final Direction direction) { + public boolean isMovable(final Direction direction) { return rank.canMove(direction.getY()) && file.canMove(direction.getX()); } - private Position move(final Direction direction) { + public Position move(final Direction direction) { File movedFile = this.file.move(direction.getX()); Rank movedRank = this.rank.move(direction.getY()); return Position.from(movedFile, movedRank); diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java index 2165239..4b265ae 100644 --- a/src/test/java/chess/domain/piece/BishopTest.java +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -31,7 +30,7 @@ void find_paths_success(String targetPosition, Tuple expected) { Piece piece = new Bishop(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java index 73c0228..87799c5 100644 --- a/src/test/java/chess/domain/piece/KingTest.java +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -25,7 +24,7 @@ void find_paths_success(String targetPosition) { Piece piece = new King(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java index 2629bcc..e856edd 100644 --- a/src/test/java/chess/domain/piece/KnightTest.java +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -25,7 +24,7 @@ void find_paths_success(String targetPosition) { Piece piece = new Knight(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); diff --git a/src/test/java/chess/domain/piece/MovePatternTest.java b/src/test/java/chess/domain/piece/MovePatternTest.java index cc7230a..900c980 100644 --- a/src/test/java/chess/domain/piece/MovePatternTest.java +++ b/src/test/java/chess/domain/piece/MovePatternTest.java @@ -24,7 +24,7 @@ void pawn_pattern_black() { MovePattern movePattern = MovePattern.findPawnMovePattern(color); // when - Collection directions = movePattern.getCoordinates(); + Collection directions = movePattern.getDirections(); // then assertThat(directions) @@ -39,7 +39,7 @@ void pawn_pattern_white() { MovePattern movePattern = MovePattern.findPawnMovePattern(color); // when - Collection directions = movePattern.getCoordinates(); + Collection directions = movePattern.getDirections(); // then assertThat(directions) diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java index e3a7cf0..7932993 100644 --- a/src/test/java/chess/domain/piece/PawnTest.java +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -25,7 +24,7 @@ void find_paths_success_move_count_one_on_initial_move(String sourcePosition, St Piece piece = new Pawn(color); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); @@ -41,7 +40,7 @@ void find_paths_success_move_count_two_on_initial_move(String sourcePosition, St Piece piece = new Pawn(color); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).containsOnly(Position.of(expected)); @@ -58,7 +57,7 @@ void find_paths_success_move_count_one(String sourcePosition, String targetPosit Piece piece = new Pawn(color); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).isEmpty(); diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java index e3b4eda..2205118 100644 --- a/src/test/java/chess/domain/piece/QueenTest.java +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -13,7 +13,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -32,7 +31,7 @@ void find_paths_success_diagonal(String targetPosition, Tuple expected) { Piece piece = new Queen(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") @@ -49,7 +48,7 @@ void find_paths_success_cardinal(String targetPosition, Tuple expected) { Piece piece = new Queen(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then //then diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java index 94e38e1..30bda79 100644 --- a/src/test/java/chess/domain/piece/RookTest.java +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Set; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -31,7 +30,7 @@ void find_paths_success(String targetPosition, Tuple expected) { Piece piece = new Rook(Color.WHITE); //when - Set paths = piece.findPath(source, target); + Collection paths = piece.findPath(source, target); //then assertThat(paths).extracting("file", "rank") diff --git a/src/test/java/chess/domain/player/PositionTest.java b/src/test/java/chess/domain/player/PositionTest.java index f95a8d8..3cbbacc 100644 --- a/src/test/java/chess/domain/player/PositionTest.java +++ b/src/test/java/chess/domain/player/PositionTest.java @@ -4,15 +4,6 @@ import chess.domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; -import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -49,40 +40,6 @@ void from_key() { .containsOnly(file, rank); } - @Test - @DisplayName("거쳐가는 모든 위치를 반환한다.") - void find_passing_positions() { - // given - Position source = Position.of("d4"); - Position target = Position.of("d7"); - Direction direction = Direction.NORTH; - - // when - Set positions = source.findPassingPositions(target, direction); - - // then - assertThat(positions) - .hasSize(2) - .containsExactlyInAnyOrder(Position.of("d5"), Position.of("d6")); - } - - @ParameterizedTest - @MethodSource("createParametersForAvailablePositions") - @DisplayName("이동 가능한 위치를 반환한다.") - void find_available_positions(boolean canMoveInfinitely, Collection expected) { - // given - Position position = Position.of("d4"); - Direction direction = Direction.EAST; - - // when - Collection positions = position.findAvailablePositions(direction, canMoveInfinitely); - - // then - assertThat(positions) - .hasSize(expected.size()) - .containsAll(expected); - } - @Test @DisplayName("랭크가 동일한지 확인한다.") void has_same_rank() { @@ -96,11 +53,4 @@ void has_same_rank() { // then assertThat(hasSameRank).isTrue(); } - - private static Stream createParametersForAvailablePositions() { - return Stream.of( - Arguments.of(true, Arrays.asList(Position.of("e4"), Position.of("f4"), Position.of("g4"), Position.of("h4"))), - Arguments.of(false, Collections.singletonList(Position.of("e4"))) - ); - } } From 3347861688f01020b69b9ba28fa3219eca50b65a Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Fri, 3 Sep 2021 03:10:15 +0900 Subject: [PATCH 37/42] =?UTF-8?q?refactor:=20Set=20->=20Collection=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B0=98=ED=99=98=20=EB=B0=8F=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/board/Board.java | 9 +- src/main/java/chess/domain/player/Player.java | 96 +++++++++++-------- .../java/chess/domain/player/PlayerTest.java | 6 +- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index f0f7109..179c2b3 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -6,7 +6,7 @@ import chess.domain.player.Player; import chess.domain.player.Position; -import java.util.Set; +import java.util.Collection; public class Board { private final Player white; @@ -28,7 +28,7 @@ public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) validateTarget(player, target); validateKingMovable(player, enemy, source, target); - enemy.attacked(target); + enemy.removePieceOn(target); movePiece(player, source, target); } @@ -64,13 +64,12 @@ private void validateKingMovable(final Player player, final Player enemy, final } private void movePiece(final Player player, final Position source, final Position target) { - Set paths = player.findPaths(source, target); + Collection paths = player.findPaths(source, target); validatePathsEmpty(paths); - player.update(source, target); } - private void validatePathsEmpty(final Set paths) { + private void validatePathsEmpty(final Collection paths) { boolean isWhiteBlocked = paths.stream() .anyMatch(white::hasPieceOn); boolean isBlackBlocked = paths.stream() diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index 4710c52..8d63521 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -4,72 +4,92 @@ import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.piece.PieceFactory; -import chess.domain.piece.type.PieceType; +import chess.domain.piece.PieceResolver; -import java.util.*; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public class Player { private final Map pieces; - private final AttackPositions attackPositions; + private final AttackRange attackRange; public Player(final Color color) { pieces = new HashMap<>(PieceFactory.createPieces(color)); - attackPositions = new AttackPositions(pieces); + attackRange = new AttackRange(pieces); } public double sumScores() { - List pawnPositions = findPawnPositions(); - - double pawnScores = calculatePawnScores(pawnPositions); + double pawnScores = calculatePawnScores(); double scoresExceptPawn = calculateScoresExceptPawn(); + return pawnScores + scoresExceptPawn; } + private double calculatePawnScores() { + List pawnPositions = findPawnPositions(); + Map pawnCountsOnSameFile = extractPawnCountsOnSameFile(pawnPositions); + + return pawnCountsOnSameFile.values() + .stream() + .mapToDouble(PieceResolver::sumPawnScores) + .sum(); + } + private List findPawnPositions() { return pieces.keySet() .stream() .filter(position -> { Piece piece = pieces.get(position); - return PieceType.isPawn(piece); + return piece.isPawn(); }) .collect(Collectors.toList()); } - private double calculatePawnScores(final List pawnPositions) { - Map pawnCount = new EnumMap<>(File.class); + private Map extractPawnCountsOnSameFile(final List pawnPositions) { + Map pawnCountOnSameFile = new EnumMap<>(File.class); + pawnPositions.stream() .map(Position::getFile) - .forEach(file -> pawnCount.put(file, pawnCount.getOrDefault(file, 0) + 1)); + .forEach(file -> pawnCountOnSameFile.put(file, pawnCountOnSameFile.getOrDefault(file, 0) + 1)); - return pawnCount.values() - .stream() - .mapToDouble(PieceType::sumPawnScores) - .sum(); + return pawnCountOnSameFile; } private double calculateScoresExceptPawn() { return pieces.values() .stream() - .filter(piece -> !PieceType.isPawn(piece)) - .mapToDouble(PieceType::findScoreBy) + .filter(Piece::isNotPawn) + .mapToDouble(PieceResolver::findScoreBy) .sum(); } - public boolean hasPieceOn(final Position position) { - return pieces.containsKey(position); - } - - public Set findPaths(final Position source, final Position target) { + public Collection findPaths(final Position source, final Position target) { Piece sourcePiece = findPieceBy(source); + return sourcePiece.findPath(source, target); } + public Piece findPieceBy(final Position position) { + if (isEmptyOn(position)) { + throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); + } + + return pieces.get(position); + } + + private boolean isEmptyOn(final Position position) { + return !pieces.containsKey(position); + } + public void update(final Position source, final Position target) { Piece sourcePiece = findPieceBy(source); movePiece(source, target, sourcePiece); - attackPositions.update(source, target, sourcePiece); + attackRange.update(source, target, sourcePiece); } private void movePiece(final Position source, final Position target, final Piece sourcePiece) { @@ -77,33 +97,31 @@ private void movePiece(final Position source, final Position target, final Piece pieces.remove(source); } - public Piece findPieceBy(final Position position) { - if (!hasPieceOn(position)) { - throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); - } - - return pieces.get(position); - } + public boolean hasKingOn(final Position position) { + Piece piece = findPieceBy(position); - public boolean hasKingOn(Position position) { - return PieceType.isKing(findPieceBy(position)); + return piece.isKing(); } public boolean isKingDead() { return pieces.values().stream() - .noneMatch(PieceType::isKing); + .noneMatch(Piece::isKing); } - public boolean canAttack(Position position) { - return attackPositions.contains(position); + public boolean canAttack(final Position position) { + return attackRange.contains(position); } - public void attacked(final Position target) { - if (!hasPieceOn(target)) { + public void removePieceOn(final Position position) { + if (isEmptyOn(position)) { return; } - attackPositions.remove(target, pieces.get(target)); - pieces.remove(target); + attackRange.remove(position, pieces.get(position)); + pieces.remove(position); + } + + public boolean hasPieceOn(final Position position) { + return pieces.containsKey(position); } } diff --git a/src/test/java/chess/domain/player/PlayerTest.java b/src/test/java/chess/domain/player/PlayerTest.java index 56d64aa..eaaba9c 100644 --- a/src/test/java/chess/domain/player/PlayerTest.java +++ b/src/test/java/chess/domain/player/PlayerTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.util.Set; +import java.util.Collection; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -71,7 +71,7 @@ void find_paths(Color color, String sourcePosition, String targetPosition, Strin Position path = Position.of(expected); //when - Set paths = player.findPaths(source, target); + Collection paths = player.findPaths(source, target); //then assertThat(paths).containsOnly(path); @@ -127,7 +127,7 @@ void sum_scores() { void is_king_dead() { //given Player player = new Player(Color.WHITE); - player.attacked(Position.of("e1")); + player.removePieceOn(Position.of("e1")); //when boolean kingDead = player.isKingDead(); From 361883e265bc36bf8c2800138aa643dc3d122c9e Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Sat, 4 Sep 2021 04:16:05 +0900 Subject: [PATCH 38/42] =?UTF-8?q?refactor:=20MoveParameters=20->=20MoveOpt?= =?UTF-8?q?ions=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/ChessGame.java | 6 +-- src/main/java/chess/domain/board/Board.java | 8 ++-- .../{MoveParameters.java => MoveOptions.java} | 8 ++-- .../java/chess/domain/board/BoardTest.java | 44 +++++++++--------- .../chess/domain/command/MoveOptionsTest.java | 45 +++++++++++++++++++ 5 files changed, 78 insertions(+), 33 deletions(-) rename src/main/java/chess/domain/command/{MoveParameters.java => MoveOptions.java} (63%) create mode 100644 src/test/java/chess/domain/command/MoveOptionsTest.java diff --git a/src/main/java/chess/domain/ChessGame.java b/src/main/java/chess/domain/ChessGame.java index 0828ad7..4ba805d 100644 --- a/src/main/java/chess/domain/ChessGame.java +++ b/src/main/java/chess/domain/ChessGame.java @@ -2,7 +2,7 @@ import chess.domain.board.Board; import chess.domain.board.Status; -import chess.domain.command.MoveParameters; +import chess.domain.command.MoveOptions; public class ChessGame { @@ -13,8 +13,8 @@ public class ChessGame { public ChessGame() { } - public void move(final MoveParameters moveParameters) { - board.move(moveParameters, isWhiteTurn); + public void move(final MoveOptions moveOptions) { + board.move(moveOptions, isWhiteTurn); isWhiteTurn = !isWhiteTurn; if (board.isEnd()) { diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index 179c2b3..77db463 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -1,6 +1,6 @@ package chess.domain.board; -import chess.domain.command.MoveParameters; +import chess.domain.command.MoveOptions; import chess.domain.piece.Color; import chess.domain.piece.Piece; import chess.domain.player.Player; @@ -17,11 +17,11 @@ public Board() { this.black = new Player(Color.BLACK); } - public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) { + public void move(final MoveOptions moveOptions, final boolean isWhiteTurn) { Player player = currentPlayer(isWhiteTurn); Player enemy = currentPlayer(!isWhiteTurn); - Position source = moveParameters.getSource(); - Position target = moveParameters.getTarget(); + Position source = moveOptions.getSource(); + Position target = moveOptions.getTarget(); validateSourceOwner(enemy, source); validateSamePosition(source, target); diff --git a/src/main/java/chess/domain/command/MoveParameters.java b/src/main/java/chess/domain/command/MoveOptions.java similarity index 63% rename from src/main/java/chess/domain/command/MoveParameters.java rename to src/main/java/chess/domain/command/MoveOptions.java index c8a607a..c06b15b 100644 --- a/src/main/java/chess/domain/command/MoveParameters.java +++ b/src/main/java/chess/domain/command/MoveOptions.java @@ -4,7 +4,7 @@ import java.util.List; -public class MoveParameters { +public class MoveOptions { private static final int SOURCE_INDEX = 0; private static final int TARGET_INDEX = 1; @@ -12,11 +12,11 @@ public class MoveParameters { private final Position source; private final Position target; - public MoveParameters(final List parameters) { - this(Position.of(parameters.get(SOURCE_INDEX)), Position.of(parameters.get(TARGET_INDEX))); + public MoveOptions(final List options) { + this(Position.of(options.get(SOURCE_INDEX)), Position.of(options.get(TARGET_INDEX))); } - public MoveParameters(final Position source, final Position target) { + public MoveOptions(final Position source, final Position target) { this.source = source; this.target = target; } diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java index 2585f02..8a5eb44 100644 --- a/src/test/java/chess/domain/board/BoardTest.java +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -1,6 +1,6 @@ package chess.domain.board; -import chess.domain.command.MoveParameters; +import chess.domain.command.MoveOptions; import chess.domain.player.Position; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -34,11 +34,11 @@ void move_source_position_empty() { Board board = new Board(); Position source = Position.of("b3"); Position target = Position.of("b4"); - MoveParameters moveParameters = new MoveParameters(source, target); + MoveOptions moveOptions = new MoveOptions(source, target); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, true)) + .isThrownBy(() -> board.move(moveOptions, true)) .withMessage("해당 위치에 기물이 존재하지 않습니다."); } @@ -50,11 +50,11 @@ void move_source_not_owner(String sourcePosition, String targetPosition, boolean Board board = new Board(); Position source = Position.of(sourcePosition); Position target = Position.of(targetPosition); - MoveParameters moveParameters = new MoveParameters(source, target); + MoveOptions moveOptions = new MoveOptions(source, target); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .isThrownBy(() -> board.move(moveOptions, isWhiteTurn)) .withMessage("자신의 기물만 움직일 수 있습니다."); } @@ -66,11 +66,11 @@ void move_source_and_target_same_color(String sourcePosition, String targetPosit Board board = new Board(); Position source = Position.of(sourcePosition); Position target = Position.of(targetPosition); - MoveParameters moveParameters = new MoveParameters(source, target); + MoveOptions moveOptions = new MoveOptions(source, target); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .isThrownBy(() -> board.move(moveOptions, isWhiteTurn)) .withMessage("같은 색상의 기물은 공격할 수 없습니다."); } @@ -82,11 +82,11 @@ void move_source_and_target_same(String sourcePosition, String targetPosition, b Board board = new Board(); Position source = Position.of(sourcePosition); Position target = Position.of(targetPosition); - MoveParameters moveParameters = new MoveParameters(source, target); + MoveOptions moveOptions = new MoveOptions(source, target); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .isThrownBy(() -> board.move(moveOptions, isWhiteTurn)) .withMessage("출발 위치와 도착 위치가 같을 수 없습니다."); } @@ -98,11 +98,11 @@ void move_invalid_paths(String sourcePosition, String targetPosition, boolean is Board board = new Board(); Position source = Position.of(sourcePosition); Position target = Position.of(targetPosition); - MoveParameters moveParameters = new MoveParameters(source, target); + MoveOptions moveOptions = new MoveOptions(source, target); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, isWhiteTurn)) + .isThrownBy(() -> board.move(moveOptions, isWhiteTurn)) .withMessage("기물을 통과하여 이동할 수 없습니다."); } @@ -112,11 +112,11 @@ void move_invalid_paths(String sourcePosition, String targetPosition, boolean is void move_king_invalid_target(String source, String target) { //given Board board = setBoardToAttackKing(); - MoveParameters moveParameters = new MoveParameters(Position.of(source), Position.of(target)); + MoveOptions moveOptions = new MoveOptions(Position.of(source), Position.of(target)); //when, then assertThatIllegalArgumentException() - .isThrownBy(() -> board.move(moveParameters, true)) + .isThrownBy(() -> board.move(moveOptions, true)) .withMessage("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다."); } @@ -136,20 +136,20 @@ void get_status() { private Board setBoardToGetStatus() { Board board = new Board(); - board.move(new MoveParameters(Position.of("e7"), Position.of("e5")), false); - board.move(new MoveParameters(Position.of("e5"), Position.of("e4")), false); - board.move(new MoveParameters(Position.of("e4"), Position.of("e3")), false); - board.move(new MoveParameters(Position.of("d2"), Position.of("e3")), true); + board.move(new MoveOptions(Position.of("e7"), Position.of("e5")), false); + board.move(new MoveOptions(Position.of("e5"), Position.of("e4")), false); + board.move(new MoveOptions(Position.of("e4"), Position.of("e3")), false); + board.move(new MoveOptions(Position.of("d2"), Position.of("e3")), true); return board; } private Board setBoardToAttackKing() { Board board = new Board(); - board.move(new MoveParameters(Position.of("e2"), Position.of("e4")), true); - board.move(new MoveParameters(Position.of("d2"), Position.of("d4")), true); - board.move(new MoveParameters(Position.of("e1"), Position.of("e2")), true); - board.move(new MoveParameters(Position.of("c7"), Position.of("c5")), false); - board.move(new MoveParameters(Position.of("d8"), Position.of("a5")), false); + board.move(new MoveOptions(Position.of("e2"), Position.of("e4")), true); + board.move(new MoveOptions(Position.of("d2"), Position.of("d4")), true); + board.move(new MoveOptions(Position.of("e1"), Position.of("e2")), true); + board.move(new MoveOptions(Position.of("c7"), Position.of("c5")), false); + board.move(new MoveOptions(Position.of("d8"), Position.of("a5")), false); return board; } } diff --git a/src/test/java/chess/domain/command/MoveOptionsTest.java b/src/test/java/chess/domain/command/MoveOptionsTest.java new file mode 100644 index 0000000..28163e4 --- /dev/null +++ b/src/test/java/chess/domain/command/MoveOptionsTest.java @@ -0,0 +1,45 @@ +package chess.domain.command; + +import chess.domain.board.File; +import chess.domain.board.Rank; +import chess.domain.player.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class MoveOptionsTest { + + @Test + @DisplayName("시작 위치를 반환한다.") + void getSource() { + //given + List options = Arrays.asList("a1", "b2"); + MoveOptions moveOptions = new MoveOptions(options); + + //when + Position source = moveOptions.getSource(); + + //then + assertThat(source.getFile()).isEqualTo(File.A); + assertThat(source.getRank()).isEqualTo(Rank.R1); + } + + @Test + @DisplayName("도착 위치를 반환한다.") + void getTarget() { + //given + List options = Arrays.asList("a1", "b2"); + MoveOptions moveOptions = new MoveOptions(options); + + //when + Position target = moveOptions.getTarget(); + + //then + assertThat(target.getFile()).isEqualTo(File.B); + assertThat(target.getRank()).isEqualTo(Rank.R2); + } +} From d6edf8583e7dfed06805fded051f1467750073f7 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Sat, 4 Sep 2021 04:18:00 +0900 Subject: [PATCH 39/42] =?UTF-8?q?refactor:=20=EB=AA=85=EB=A0=B9=EC=96=B4?= =?UTF-8?q?=20enum=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=ED=8F=AC=ED=95=A8=EB=90=9C=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=20=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chess/controller/ChessController.java | 83 ++++++++++------- .../java/chess/domain/command/Command.java | 65 +++++++------- .../chess/domain/command/CommandOptions.java | 76 ++++++++++++++++ .../domain/command/CommandOptionsTest.java | 88 +++++++++++++++++++ .../chess/domain/command/CommandTest.java | 78 ++++++++++++++++ 5 files changed, 324 insertions(+), 66 deletions(-) create mode 100644 src/main/java/chess/domain/command/CommandOptions.java create mode 100644 src/test/java/chess/domain/command/CommandOptionsTest.java create mode 100644 src/test/java/chess/domain/command/CommandTest.java diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index 68dce81..c7984ba 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -3,10 +3,13 @@ import chess.controller.dto.BoardDto; import chess.domain.ChessGame; import chess.domain.board.Status; -import chess.domain.command.Command; +import chess.domain.command.CommandOptions; +import chess.domain.command.MoveOptions; import chess.view.InputView; import chess.view.OutputView; +import java.util.List; + public class ChessController { private final InputView inputView; @@ -18,50 +21,57 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - outputView.printGuide(); - ChessGame chessGame = new ChessGame(); + ChessGame chessGame = initialize(); while (chessGame.isRunning()) { - try { - outputView.printTurn(chessGame.isWhiteTurn()); - Command command = new Command(inputView.getCommand()); - runCommand(chessGame, command); - printBoard(chessGame); - } catch (UnsupportedOperationException e) { - System.out.println(e.getMessage()); - } + play(chessGame); } - printBoard(chessGame); - printStatus(chessGame); + printResult(chessGame); + } + + private ChessGame initialize() { + outputView.printGuide(); + ChessGame chessGame = new ChessGame(); + CommandOptions initialCommandOptions = CommandOptions.of(inputView.getCommand()); + executeInitialCommand(initialCommandOptions, chessGame); + + return chessGame; + } + + private void executeInitialCommand(final CommandOptions commandOptions, final ChessGame chessGame) { + commandOptions.validateInitialCommand(); + + if (commandOptions.isEnd()) { + chessGame.end(); + } } - private void runCommand(final ChessGame chessGame, final Command command) { + private void play(final ChessGame chessGame) { try { - if (command.isStart()) { - return; - } - - if (command.isEnd()) { - chessGame.end(); - return; - } - - if (command.isMove()) { - chessGame.move(command.getMoveParameters()); - return; - } - - if (command.isStatus()) { - printStatus(chessGame); - return; - } + outputView.printTurn(chessGame.isWhiteTurn()); + CommandOptions commandOptions = CommandOptions.of(inputView.getCommand()); + executeCommand(chessGame, commandOptions); + printBoard(chessGame); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); - return; + } + } + + private void executeCommand(final ChessGame chessGame, final CommandOptions commandOptions) { + if (commandOptions.isEnd()) { + chessGame.end(); + } + + if (commandOptions.isMove()) { + List options = commandOptions.getOptions(); + MoveOptions moveOptions = new MoveOptions(options); + chessGame.move(moveOptions); } - throw new UnsupportedOperationException("유효하지 않은 명령어입니다."); + if (commandOptions.isStatus()) { + printStatus(chessGame); + } } private void printBoard(final ChessGame chessGame) { @@ -73,4 +83,9 @@ private void printStatus(final ChessGame chessGame) { Status status = chessGame.getStatus(); outputView.printStatus(status); } + + private void printResult(final ChessGame chessGame) { + printBoard(chessGame); + printStatus(chessGame); + } } diff --git a/src/main/java/chess/domain/command/Command.java b/src/main/java/chess/domain/command/Command.java index 5aff42b..66662cf 100644 --- a/src/main/java/chess/domain/command/Command.java +++ b/src/main/java/chess/domain/command/Command.java @@ -1,57 +1,58 @@ package chess.domain.command; import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import java.util.HashMap; +import java.util.Map; -public class Command { +public enum Command { - private static final String DELIMITER = " "; - private static final int COMMAND_INDEX = 0; + START("start"), + END("end"), + MOVE("move"), + STATUS("status"); - private static final String START = "start"; - private static final String END = "end"; - private static final String MOVE = "move"; - private static final String STATUS = "status"; + private static final Map COMMANDS = createCommands(); + + private static Map createCommands() { + Map commands = new HashMap<>(); + + Arrays.stream(values()) + .forEach(command -> commands.put(command.command, command)); + + return commands; + } private final String command; - private final List parameters; - public Command(final String commandLine) { - List chunks = Arrays.stream(commandLine.split(DELIMITER)) - .map(String::trim) - .map(String::toLowerCase) - .collect(Collectors.toList()); + Command(final String command) { + this.command = command; + } + + public static Command of(final String command) { + Command foundCommand = COMMANDS.get(command); - if (chunks.isEmpty()) { - throw new IllegalArgumentException(); + if (foundCommand == null) { + throw new IllegalArgumentException("유효하지 않은 명령어입니다."); } - this.command = chunks.get(COMMAND_INDEX); - this.parameters = chunks.subList(COMMAND_INDEX + 1, chunks.size()); + return foundCommand; } - public boolean isStart() { - return command.equals(START); + public void validateInitialCommand() { + if (isStatus() || isMove()) { + throw new IllegalArgumentException(String.format("%s 또는 %s를 입력해주세요.", START.command, END.command)); + } } public boolean isEnd() { - return command.equals(END); + return this.equals(END); } public boolean isMove() { - return command.equals(MOVE); + return this.equals(MOVE); } public boolean isStatus() { - return command.equals(STATUS); - } - - public MoveParameters getMoveParameters() { - if (parameters.size() != 2) { - throw new IllegalArgumentException(); - } - - return new MoveParameters(parameters); + return this.equals(STATUS); } } diff --git a/src/main/java/chess/domain/command/CommandOptions.java b/src/main/java/chess/domain/command/CommandOptions.java new file mode 100644 index 0000000..bd27dd3 --- /dev/null +++ b/src/main/java/chess/domain/command/CommandOptions.java @@ -0,0 +1,76 @@ +package chess.domain.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class CommandOptions { + + private static final String DELIMITER = " "; + private static final int COMMAND_INDEX = 0; + private static final int CHECK_VALUE_IF_COMMAND_HAS_OPTIONS = 1; + private static final int FIRST_OPTION_INDEX = 1; + + private final Command command; + private final List options; + + private CommandOptions(final Command command, final List options) { + this.command = command; + this.options = options; + } + + public static CommandOptions of(final String text) { + validateEmpty(text); + List commandAndOptions = split(text); + Command command = Command.of(commandAndOptions.get(COMMAND_INDEX)); + List options = extractOptions(commandAndOptions); + + return new CommandOptions(command, options); + } + + private static void validateEmpty(final String text) { + if (text == null) { + throw new IllegalArgumentException("명령어가 존재하지 않습니다."); + } + } + + private static List split(final String text) { + return Arrays.stream(text.split(DELIMITER)) + .map(String::trim) + .map(String::toLowerCase) + .collect(Collectors.toList()); + } + + private static List extractOptions(final List commandAndOptions) { + if (hasOptions(commandAndOptions)) { + return commandAndOptions.subList(FIRST_OPTION_INDEX, commandAndOptions.size()); + } + + return Collections.emptyList(); + } + + private static boolean hasOptions(final List splitText) { + return splitText.size() > CHECK_VALUE_IF_COMMAND_HAS_OPTIONS; + } + + public void validateInitialCommand() { + command.validateInitialCommand(); + } + + public boolean isEnd() { + return command.isEnd(); + } + + public boolean isMove() { + return command.isMove(); + } + + public boolean isStatus() { + return command.isStatus(); + } + + public List getOptions() { + return Collections.unmodifiableList(options); + } +} diff --git a/src/test/java/chess/domain/command/CommandOptionsTest.java b/src/test/java/chess/domain/command/CommandOptionsTest.java new file mode 100644 index 0000000..9774bd1 --- /dev/null +++ b/src/test/java/chess/domain/command/CommandOptionsTest.java @@ -0,0 +1,88 @@ +package chess.domain.command; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CommandOptionsTest { + + @ParameterizedTest + @CsvSource(value = {"start, 0", "end, 0", "move b2 b3, 2", "status, 0"}) + @DisplayName("옵션이 담긴 명령어를 반환한다.") + void of(String text, int expected) { + //when + CommandOptions commandOptions = CommandOptions.of(text); + + //then + assertThat(commandOptions.getOptions()).hasSize(expected); + } + + @ParameterizedTest + @NullSource + @DisplayName("명령어가 null일 경우 예외가 발생한다.") + void of_fail(String text) { + //when //then + assertThatThrownBy(() -> CommandOptions.of(text)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("명령어가 존재하지 않습니다."); + } + + @ParameterizedTest + @CsvSource(value = {"status, move"}) + @DisplayName("시작 또는 종료 명령어가 아닐 경우 예외가 발생한다.") + void validateInitialCommand(String text) { + //given + CommandOptions commandOptions = CommandOptions.of(text); + + //when // then + assertThatThrownBy(commandOptions::validateInitialCommand) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("start 또는 end를 입력해주세요."); + } + + @ParameterizedTest + @CsvSource(value = {"start, false", "end, true", "move, false", "status, false"}) + @DisplayName("종료 명령어인지 확인한다.") + void isEnd(String text, boolean expected) { + //given + CommandOptions commandOptions = CommandOptions.of(text); + + //when + boolean actual = commandOptions.isEnd(); + + //then + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource(value = {"start, false", "end, false", "move, true", "status, false"}) + @DisplayName("종료 명령어인지 확인한다.") + void isMove(String text, boolean expected) { + //given + CommandOptions commandOptions = CommandOptions.of(text); + + //when + boolean actual = commandOptions.isMove(); + + //then + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource(value = {"start, false", "end, false", "move, false", "status, true"}) + @DisplayName("종료 명령어인지 확인한다.") + void isStatus(String text, boolean expected) { + //given + CommandOptions commandOptions = CommandOptions.of(text); + + //when + boolean actual = commandOptions.isStatus(); + + //then + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/chess/domain/command/CommandTest.java b/src/test/java/chess/domain/command/CommandTest.java new file mode 100644 index 0000000..657d774 --- /dev/null +++ b/src/test/java/chess/domain/command/CommandTest.java @@ -0,0 +1,78 @@ +package chess.domain.command; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CommandTest { + + @ParameterizedTest + @CsvSource(value = {"start, START", "end, END", "move, MOVE", "status, STATUS"}) + @DisplayName("명령어를 반환한다.") + void of(String text, Command expectedCommand) { + //when + Command command = Command.of(text); + + //then + assertThat(command).isEqualTo(expectedCommand); + } + + @ParameterizedTest + @NullSource + @ValueSource(strings = {"star", "reset", "undo", "redo", "show"}) + @DisplayName("명령어를 찾을 수 없을 경우 예외가 발생한다.") + void of_fail(String text) { + //when //then + assertThatThrownBy(() -> Command.of(text)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 명령어입니다."); + } + + @ParameterizedTest + @CsvSource(value = {"STATUS, MOVE"}) + @DisplayName("시작 또는 종료 명령어가 아닐 경우 예외가 발생한다.") + void validateInitialCommand(Command command) { + //when // then + assertThatThrownBy(command::validateInitialCommand) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("start 또는 end를 입력해주세요."); + } + + @ParameterizedTest + @CsvSource(value = {"START, false", "END, true", "MOVE, false", "STATUS, false"}) + @DisplayName("종료 명령어인지 확인한다.") + void isEnd(Command command, boolean expected) { + //when + boolean actual = command.isEnd(); + + //then + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource(value = {"START, false", "END, false", "MOVE, true", "STATUS, false"}) + @DisplayName("종료 명령어인지 확인한다.") + void isMove(Command command, boolean expected) { + //when + boolean actual = command.isMove(); + + //then + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource(value = {"START, false", "END, false", "MOVE, false", "STATUS, true"}) + @DisplayName("종료 명령어인지 확인한다.") + void isStatus(Command command, boolean expected) { + //when + boolean actual = command.isStatus(); + + //then + assertThat(actual).isEqualTo(expected); + } +} From 3ef6adbda89a595f5cef6499ba0d0cbabf0a2aa8 Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Sat, 4 Sep 2021 04:31:48 +0900 Subject: [PATCH 40/42] =?UTF-8?q?refactor:=20=EB=AA=85=EB=A0=B9=EC=96=B4?= =?UTF-8?q?=20enum=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=ED=8F=AC=ED=95=A8=EB=90=9C=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=20=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/board/Board.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java index 77db463..32f2a3b 100644 --- a/src/main/java/chess/domain/board/Board.java +++ b/src/main/java/chess/domain/board/Board.java @@ -23,10 +23,7 @@ public void move(final MoveOptions moveOptions, final boolean isWhiteTurn) { Position source = moveOptions.getSource(); Position target = moveOptions.getTarget(); - validateSourceOwner(enemy, source); - validateSamePosition(source, target); - validateTarget(player, target); - validateKingMovable(player, enemy, source, target); + validate(player, enemy, source, target); enemy.removePieceOn(target); movePiece(player, source, target); @@ -39,6 +36,13 @@ private Player currentPlayer(final boolean isWhiteTurn) { return black; } + private void validate(final Player player, final Player enemy, final Position source, final Position target) { + validateSourceOwner(enemy, source); + validateSamePosition(source, target); + validateTarget(player, target); + validateKingMovable(player, enemy, source, target); + } + private void validateSourceOwner(final Player enemy, final Position source) { if (enemy.hasPieceOn(source)) { throw new IllegalArgumentException("자신의 기물만 움직일 수 있습니다."); From b21c36a1880278819998dd050ab2da88a8c2f4fc Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Mon, 13 Sep 2021 20:38:07 +0900 Subject: [PATCH 41/42] =?UTF-8?q?refactor:=20CommandOptions=EC=97=90?= =?UTF-8?q?=EC=84=9C=20MoveOptions=20=EB=B0=98=ED=99=98=ED=95=98=EA=B2=8C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chess/controller/ChessController.java | 9 ++--- .../chess/domain/command/CommandOptions.java | 4 +-- .../domain/command/CommandOptionsTest.java | 33 +++++++++++++++++-- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index c7984ba..aa70151 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -4,12 +4,9 @@ import chess.domain.ChessGame; import chess.domain.board.Status; import chess.domain.command.CommandOptions; -import chess.domain.command.MoveOptions; import chess.view.InputView; import chess.view.OutputView; -import java.util.List; - public class ChessController { private final InputView inputView; @@ -61,12 +58,12 @@ private void play(final ChessGame chessGame) { private void executeCommand(final ChessGame chessGame, final CommandOptions commandOptions) { if (commandOptions.isEnd()) { chessGame.end(); + return; } if (commandOptions.isMove()) { - List options = commandOptions.getOptions(); - MoveOptions moveOptions = new MoveOptions(options); - chessGame.move(moveOptions); + chessGame.move(commandOptions.getMoveOptions()); + return; } if (commandOptions.isStatus()) { diff --git a/src/main/java/chess/domain/command/CommandOptions.java b/src/main/java/chess/domain/command/CommandOptions.java index bd27dd3..a46abce 100644 --- a/src/main/java/chess/domain/command/CommandOptions.java +++ b/src/main/java/chess/domain/command/CommandOptions.java @@ -70,7 +70,7 @@ public boolean isStatus() { return command.isStatus(); } - public List getOptions() { - return Collections.unmodifiableList(options); + public MoveOptions getMoveOptions() { + return new MoveOptions(options); } } diff --git a/src/test/java/chess/domain/command/CommandOptionsTest.java b/src/test/java/chess/domain/command/CommandOptionsTest.java index 9774bd1..22532de 100644 --- a/src/test/java/chess/domain/command/CommandOptionsTest.java +++ b/src/test/java/chess/domain/command/CommandOptionsTest.java @@ -1,9 +1,11 @@ package chess.domain.command; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -11,14 +13,14 @@ class CommandOptionsTest { @ParameterizedTest - @CsvSource(value = {"start, 0", "end, 0", "move b2 b3, 2", "status, 0"}) + @CsvSource(value = {"start", "end", "move b2 b3", "status"}) @DisplayName("옵션이 담긴 명령어를 반환한다.") - void of(String text, int expected) { + void of(String text) { //when CommandOptions commandOptions = CommandOptions.of(text); //then - assertThat(commandOptions.getOptions()).hasSize(expected); + assertThat(commandOptions).isNotNull(); } @ParameterizedTest @@ -85,4 +87,29 @@ void isStatus(String text, boolean expected) { //then assertThat(actual).isEqualTo(expected); } + + @Test + @DisplayName("이동 명령 관련 옵션을 반환한다.") + void getMoveOptions() { + //given + CommandOptions commandOptions = CommandOptions.of("move b2 b3"); + + //when + MoveOptions moveOptions = commandOptions.getMoveOptions(); + + //then + assertThat(moveOptions).isNotNull(); + } + + @ParameterizedTest + @ValueSource(strings = {"start", "end", "status"}) + @DisplayName("이동 명령이 아닌 다른 명령이 이동 관련 옵션을 반환하려고 할 경우 예외가 발생한다.") + void getMoveOptions_fail(String text) { + //given + CommandOptions commandOptions = CommandOptions.of(text); + + //when //then + assertThatThrownBy(commandOptions::getMoveOptions) + .isInstanceOf(IndexOutOfBoundsException.class); + } } From af13054427f053a42b53b987e97839f9f015eaad Mon Sep 17 00:00:00 2001 From: Beenie93 Date: Mon, 13 Sep 2021 22:03:49 +0900 Subject: [PATCH 42/42] =?UTF-8?q?refactor:=20=ED=8F=B0=20=EA=B3=B5?= =?UTF-8?q?=EA=B2=A9=20Direction=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chess/domain/piece/MovePattern.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/chess/domain/piece/MovePattern.java b/src/main/java/chess/domain/piece/MovePattern.java index f9c76e2..e2526bd 100644 --- a/src/main/java/chess/domain/piece/MovePattern.java +++ b/src/main/java/chess/domain/piece/MovePattern.java @@ -31,12 +31,8 @@ public enum MovePattern { WHITE_PAWN_DIRECTIONS(Arrays.asList(NORTH_EAST, NORTH_WEST, NORTH)), - WHITE_PAWN_ATTACK_DIRECTIONS(Arrays.asList(NORTH_EAST, NORTH_WEST)), - BLACK_PAWN_DIRECTIONS(Arrays.asList(SOUTH_EAST, SOUTH_WEST, SOUTH)), - BLACK_PAWN_ATTACK_DIRECTIONS(Arrays.asList(SOUTH_EAST, SOUTH_WEST)), - KNIGHT_DIRECTIONS(Arrays.asList(NORTH_EAST_NORTH, NORTH_EAST_EAST, NORTH_WEST_WEST, NORTH_WEST_NORTH, SOUTH_EAST_EAST, SOUTH_EAST_SOUTH, SOUTH_WEST_SOUTH, SOUTH_WEST_WEST));