From a05391b4bcdde3dd414687a643454b6d58203694 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 17:21:15 +0900 Subject: [PATCH 01/54] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=84=9C=20=EC=B4=88=EC=95=88=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000000..f7233fd1918 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,30 @@ +# 1단계 기능 요구사항 + +## 기능 흐름 + +- [ ] 사용자로부터 게임 시작 여부 입력 + - [ ] `start`를 입력받으면 체스판을 초기화한다. + - [ ] `end`를 입력받으면 프로그램을 종료한다. +- [ ] 체스판을 초기화한다. + +## 도메인별 기능 + +### Position + +- `Rank`, `File`의 쌍을 가지고 한 칸의 위치를 표현한다. + +### Piece + +- 타입이 다른 기물들과 자신을 구별할 수 있는`PieceType`을 가진다. + +### Board + +- 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다. + +### InputView + +- `start` 명령과 `end` 명령을 입력받는다. + +### OutputView + +- 초기화 된 체스판을 출력한다. From 672926e40664a180c7cfb970d576ed1318465898 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 17:21:50 +0900 Subject: [PATCH 02/54] =?UTF-8?q?feat(position):=20Position=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/Column.java | 12 ++++++++++++ src/main/java/domain/Position.java | 5 +++++ src/main/java/domain/Row.java | 12 ++++++++++++ src/test/java/domain/PositionTest.java | 23 +++++++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 src/main/java/domain/Column.java create mode 100644 src/main/java/domain/Position.java create mode 100644 src/main/java/domain/Row.java create mode 100644 src/test/java/domain/PositionTest.java diff --git a/src/main/java/domain/Column.java b/src/main/java/domain/Column.java new file mode 100644 index 00000000000..d14d3b83a64 --- /dev/null +++ b/src/main/java/domain/Column.java @@ -0,0 +1,12 @@ +package domain; + +public enum Column { + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT +} diff --git a/src/main/java/domain/Position.java b/src/main/java/domain/Position.java new file mode 100644 index 00000000000..4feb31a7c9d --- /dev/null +++ b/src/main/java/domain/Position.java @@ -0,0 +1,5 @@ +package domain; + +public record Position(Row row, Column column) { + +} diff --git a/src/main/java/domain/Row.java b/src/main/java/domain/Row.java new file mode 100644 index 00000000000..cd5457334cc --- /dev/null +++ b/src/main/java/domain/Row.java @@ -0,0 +1,12 @@ +package domain; + +public enum Row { + A, + B, + C, + D, + E, + F, + G, + H +} diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java new file mode 100644 index 00000000000..8d739607ffe --- /dev/null +++ b/src/test/java/domain/PositionTest.java @@ -0,0 +1,23 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PositionTest { + @DisplayName("행과 열 정보를 가진 Position 인스턴스를 생성한다.") + @Test + void createPositionTest() { + // Given + Row row = Row.A; + Column col = Column.ONE; + + // When + Position position = new Position(row, col); + + // Then + assertThat(position.row()).isEqualTo(row); + assertThat(position.column()).isEqualTo(col); + } +} From 3bffae7227539b30de9979e36f2fc01e1f8ecddf Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 22:18:43 +0900 Subject: [PATCH 03/54] =?UTF-8?q?feat(inputView):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EB=A1=9C=EB=B6=80=ED=84=B0=20=EB=AA=85=EB=A0=B9?= =?UTF-8?q?=EC=9D=84=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8A=94=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 Co-authored-by: Hyunguk Ryu --- .gitmessage.txt | 4 ++++ src/main/java/domain/GameCommand.java | 5 +++++ src/main/java/dto/RequestDto.java | 16 ++++++++++++++++ src/main/java/view/InputView.java | 27 +++++++++++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 .gitmessage.txt create mode 100644 src/main/java/domain/GameCommand.java create mode 100644 src/main/java/dto/RequestDto.java create mode 100644 src/main/java/view/InputView.java diff --git a/.gitmessage.txt b/.gitmessage.txt new file mode 100644 index 00000000000..18e39abc3d9 --- /dev/null +++ b/.gitmessage.txt @@ -0,0 +1,4 @@ + + +Co-authored-by: Hyunguk Ryu + diff --git a/src/main/java/domain/GameCommand.java b/src/main/java/domain/GameCommand.java new file mode 100644 index 00000000000..28183577ab4 --- /dev/null +++ b/src/main/java/domain/GameCommand.java @@ -0,0 +1,5 @@ +package domain; + +public enum GameCommand { + START, MOVE, END; +} diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java new file mode 100644 index 00000000000..2f1c2c77d20 --- /dev/null +++ b/src/main/java/dto/RequestDto.java @@ -0,0 +1,16 @@ +package dto; + +import domain.File; +import domain.GameCommand; +import domain.Rank; + +public record RequestDto(GameCommand gameCommand, Rank rank, File file) { + public static RequestDto of(GameCommand gameCommand) { + // TODO: null 값 해결 + return new RequestDto(gameCommand, null, null); + } + + public static RequestDto of(GameCommand gameCommand, Rank rank, File file) { + return new RequestDto(gameCommand, rank, file); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000000..ca2cdc3a4cd --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,27 @@ +package view; + +import domain.GameCommand; +import dto.RequestDto; + +import java.util.Map; +import java.util.Scanner; + +public class InputView { + private static final String START_COMMAND = "start"; + private static final String END_COMMAND = "end"; + private static final Map gameCommands = Map.of( + START_COMMAND, GameCommand.START, + END_COMMAND, GameCommand.END + ); + + private final Scanner sc = new Scanner(System.in); + + public RequestDto inputGameCommand() { + String input = sc.nextLine(); + if (!gameCommands.containsKey(input)) { + throw new IllegalArgumentException("유효하지 않은 명령입니다."); + } + + return RequestDto.of(gameCommands.get(input)); + } +} From 65a1a434079991a4ac5f3920d15523bb1e5d000a Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 22:24:15 +0900 Subject: [PATCH 04/54] =?UTF-8?q?fix(position):=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EC=97=90=EC=84=9C=20=EC=B2=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=9A=A9=EC=96=B4=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/Column.java | 12 ------------ src/main/java/domain/File.java | 22 ++++++++++++++++++++++ src/main/java/domain/Position.java | 10 +++++++++- src/main/java/domain/Rank.java | 22 ++++++++++++++++++++++ src/main/java/domain/Row.java | 12 ------------ src/test/java/domain/PositionTest.java | 8 ++++---- 6 files changed, 57 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/domain/Column.java create mode 100644 src/main/java/domain/File.java create mode 100644 src/main/java/domain/Rank.java delete mode 100644 src/main/java/domain/Row.java diff --git a/src/main/java/domain/Column.java b/src/main/java/domain/Column.java deleted file mode 100644 index d14d3b83a64..00000000000 --- a/src/main/java/domain/Column.java +++ /dev/null @@ -1,12 +0,0 @@ -package domain; - -public enum Column { - ONE, - TWO, - THREE, - FOUR, - FIVE, - SIX, - SEVEN, - EIGHT -} diff --git a/src/main/java/domain/File.java b/src/main/java/domain/File.java new file mode 100644 index 00000000000..94fbe952f5f --- /dev/null +++ b/src/main/java/domain/File.java @@ -0,0 +1,22 @@ +package domain; + +public enum File { + A(0), + B(1), + C(2), + D(3), + E(4), + F(5), + G(6), + H(7); + + private final int index; + + File(final int index) { + this.index = index; + } + + public int getIndex() { + return index; + } +} diff --git a/src/main/java/domain/Position.java b/src/main/java/domain/Position.java index 4feb31a7c9d..6918ba2945e 100644 --- a/src/main/java/domain/Position.java +++ b/src/main/java/domain/Position.java @@ -1,5 +1,13 @@ package domain; -public record Position(Row row, Column column) { +// TODO: 캐싱 기법 고민하기 +public record Position(Rank rank, File file) { + public int rowIndex() { + return rank.getIndex(); + } + + public int columnIndex() { + return file.getIndex(); + } } diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java new file mode 100644 index 00000000000..6e56240dac4 --- /dev/null +++ b/src/main/java/domain/Rank.java @@ -0,0 +1,22 @@ +package domain; + +public enum Rank { + ONE(7), + TWO(6), + THREE(5), + FOUR(4), + FIVE(3), + SIX(2), + SEVEN(1), + EIGHT(0); + + private final int index; + + Rank(final int index) { + this.index = index; + } + + public int getIndex() { + return index; + } +} diff --git a/src/main/java/domain/Row.java b/src/main/java/domain/Row.java deleted file mode 100644 index cd5457334cc..00000000000 --- a/src/main/java/domain/Row.java +++ /dev/null @@ -1,12 +0,0 @@ -package domain; - -public enum Row { - A, - B, - C, - D, - E, - F, - G, - H -} diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java index 8d739607ffe..c530502cdb5 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/PositionTest.java @@ -10,14 +10,14 @@ class PositionTest { @Test void createPositionTest() { // Given - Row row = Row.A; - Column col = Column.ONE; + Rank row = Rank.ONE; + File col = File.A; // When Position position = new Position(row, col); // Then - assertThat(position.row()).isEqualTo(row); - assertThat(position.column()).isEqualTo(col); + assertThat(position.rank()).isEqualTo(row); + assertThat(position.file()).isEqualTo(col); } } From ea61d0e1b4932dfd0dbb6e488fde99edc3124783 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 22:25:23 +0900 Subject: [PATCH 05/54] =?UTF-8?q?feat(board):=20=EC=B2=B4=EC=8A=A4?= =?UTF-8?q?=ED=8C=90=EC=9D=98=20=EB=A7=90=EC=9D=84=20=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EB=A1=9C=20=EB=B0=B0=EC=B9=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=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 Co-authored-by: Hyunguk Ryu --- docs/README.md | 10 ++++----- src/main/java/domain/Board.java | 27 ++++++++++++++++++++++++ src/main/java/domain/Piece.java | 13 ++++++++++++ src/main/java/domain/PieceType.java | 32 +++++++++++++++++++++++++++++ src/test/java/domain/BoardTest.java | 19 +++++++++++++++++ 5 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 src/main/java/domain/Board.java create mode 100644 src/main/java/domain/Piece.java create mode 100644 src/main/java/domain/PieceType.java create mode 100644 src/test/java/domain/BoardTest.java diff --git a/docs/README.md b/docs/README.md index f7233fd1918..43ad440c0bc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,10 +2,10 @@ ## 기능 흐름 -- [ ] 사용자로부터 게임 시작 여부 입력 - - [ ] `start`를 입력받으면 체스판을 초기화한다. - - [ ] `end`를 입력받으면 프로그램을 종료한다. -- [ ] 체스판을 초기화한다. +- [x] 사용자로부터 게임 시작 여부 입력 + - [x] `start`를 입력받으면 체스판을 초기화한다. + - [x] `end`를 입력받으면 프로그램을 종료한다. +- [x] 체스판을 초기화한다. ## 도메인별 기능 @@ -21,7 +21,7 @@ - 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다. -### InputView +### view.InputView - `start` 명령과 `end` 명령을 입력받는다. diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java new file mode 100644 index 00000000000..8a4ed183cd0 --- /dev/null +++ b/src/main/java/domain/Board.java @@ -0,0 +1,27 @@ +package domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class Board { + private final Map chessBoard; + + public Board(final Map chessBoard) { + this.chessBoard = chessBoard; + } + + public static Board init() { + Map initialPositions = new HashMap<>(); + Arrays.stream(PieceType.values()) + .forEach(pieceType -> pieceType.getInitPosition() + .forEach(position -> initialPositions.put(position, new Piece(pieceType)))); + + return new Board(initialPositions); + } + + public Map getChessBoard() { + return Collections.unmodifiableMap(chessBoard); + } +} diff --git a/src/main/java/domain/Piece.java b/src/main/java/domain/Piece.java new file mode 100644 index 00000000000..aeb7b810868 --- /dev/null +++ b/src/main/java/domain/Piece.java @@ -0,0 +1,13 @@ +package domain; + +public class Piece { + private final PieceType pieceType; + + public Piece(final PieceType pieceType) { + this.pieceType = pieceType; + } + + public PieceType getPieceType() { + return pieceType; + } +} diff --git a/src/main/java/domain/PieceType.java b/src/main/java/domain/PieceType.java new file mode 100644 index 00000000000..d922b310e04 --- /dev/null +++ b/src/main/java/domain/PieceType.java @@ -0,0 +1,32 @@ +package domain; + +import java.util.Arrays; +import java.util.List; + +import static domain.File.*; +import static domain.Rank.*; + +public enum PieceType { + BLACK_PAWN(Arrays.stream(File.values()).map(file -> new Position(SEVEN, file)).toList()), + BLACK_ROOK(List.of(new Position(EIGHT, A), new Position(EIGHT, H))), + BLACK_KNIGHT(List.of(new Position(EIGHT, B), new Position(EIGHT, G))), + BLACK_BISHOP(List.of(new Position(EIGHT, C), new Position(EIGHT, F))), + BLACK_QUEEN(List.of(new Position(EIGHT, D))), + BLACK_KING(List.of(new Position(EIGHT, E))), + WHITE_PAWN(Arrays.stream(File.values()).map(file -> new Position(TWO, file)).toList()), + WHITE_ROOK(List.of(new Position(ONE, A), new Position(ONE, H))), + WHITE_KNIGHT(List.of(new Position(ONE, B), new Position(ONE, G))), + WHITE_BISHOP(List.of(new Position(ONE, C), new Position(ONE, F))), + WHITE_QUEEN(List.of(new Position(ONE, D))), + WHITE_KING(List.of(new Position(ONE, E))); + + private final List initPosition; + + PieceType(final List initPosition) { + this.initPosition = initPosition; + } + + public List getInitPosition() { + return initPosition; + } +} diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java new file mode 100644 index 00000000000..0b82efb3e4b --- /dev/null +++ b/src/test/java/domain/BoardTest.java @@ -0,0 +1,19 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class BoardTest { + + @DisplayName("기물의 시작 위치를 배치한 Board 인스턴스를 생성한다.") + @Test + void createBoard() { + // When + Board board = Board.init(); + + // Then + assertThat(board).isNotNull(); + } +} From 00cde4d1fbc61123d6cdf429980feea485038f99 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 19 Mar 2024 22:26:19 +0900 Subject: [PATCH 06/54] =?UTF-8?q?feat(outputView):=20=EC=B2=B4=EC=8A=A4?= =?UTF-8?q?=ED=8C=90=20=EC=B6=9C=EB=A0=A5=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 Co-authored-by: Hyunguk Ryu --- docs/README.md | 1 + src/main/java/Application.java | 13 +++++ src/main/java/controller/ChessController.java | 37 ++++++++++++++ src/main/java/domain/GameCommand.java | 2 +- src/main/java/dto/BoardDto.java | 21 ++++++++ src/main/java/view/OutputConvertor.java | 28 +++++++++++ src/main/java/view/OutputView.java | 50 +++++++++++++++++++ 7 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/main/java/Application.java create mode 100644 src/main/java/controller/ChessController.java create mode 100644 src/main/java/dto/BoardDto.java create mode 100644 src/main/java/view/OutputConvertor.java create mode 100644 src/main/java/view/OutputView.java diff --git a/docs/README.md b/docs/README.md index 43ad440c0bc..3c7873e535a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ - [x] `start`를 입력받으면 체스판을 초기화한다. - [x] `end`를 입력받으면 프로그램을 종료한다. - [x] 체스판을 초기화한다. +- [x] 체스판을 출력한다. ## 도메인별 기능 diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000000..a852778d7e2 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,13 @@ +import controller.ChessController; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + ChessController chessController = new ChessController(inputView, outputView); + + chessController.run(); + } +} diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java new file mode 100644 index 00000000000..c33a4073259 --- /dev/null +++ b/src/main/java/controller/ChessController.java @@ -0,0 +1,37 @@ +package controller; + +import domain.Board; +import domain.GameCommand; +import dto.BoardDto; +import dto.RequestDto; +import view.InputView; +import view.OutputView; + +public class ChessController { + private final InputView inputView; + private final OutputView outputView; + + public ChessController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + outputView.printWelcomeMessage(); + RequestDto requestDto = inputView.inputGameCommand(); + + if (requestDto.gameCommand() == GameCommand.END) { + return; + } + + startGame(); + } + + private void startGame() { + Board board = Board.init(); + + BoardDto boardDto = BoardDto.from(board); + + outputView.printBoard(boardDto); + } +} diff --git a/src/main/java/domain/GameCommand.java b/src/main/java/domain/GameCommand.java index 28183577ab4..f2192670994 100644 --- a/src/main/java/domain/GameCommand.java +++ b/src/main/java/domain/GameCommand.java @@ -1,5 +1,5 @@ package domain; public enum GameCommand { - START, MOVE, END; + START, MOVE, END } diff --git a/src/main/java/dto/BoardDto.java b/src/main/java/dto/BoardDto.java new file mode 100644 index 00000000000..3d8cec6e5b2 --- /dev/null +++ b/src/main/java/dto/BoardDto.java @@ -0,0 +1,21 @@ +package dto; + +import domain.Board; +import domain.PieceType; +import domain.Position; + +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +public record BoardDto(Map piecePositions) { + + public static BoardDto from(final Board board) { + Map piecePositions = board.getChessBoard() + .entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getPieceType())); + + return new BoardDto(Collections.unmodifiableMap(piecePositions)); + } +} diff --git a/src/main/java/view/OutputConvertor.java b/src/main/java/view/OutputConvertor.java new file mode 100644 index 00000000000..f6828b7e550 --- /dev/null +++ b/src/main/java/view/OutputConvertor.java @@ -0,0 +1,28 @@ +package view; + +import domain.PieceType; + +import java.util.Map; + +import static domain.PieceType.*; + +public class OutputConvertor { + private static final Map pieceFormat = Map.ofEntries( + Map.entry(BLACK_ROOK, "R"), + Map.entry(BLACK_KNIGHT, "N"), + Map.entry(BLACK_BISHOP, "B"), + Map.entry(BLACK_QUEEN, "Q"), + Map.entry(BLACK_KING, "K"), + Map.entry(BLACK_PAWN, "P"), + Map.entry(WHITE_ROOK, "r"), + Map.entry(WHITE_KNIGHT, "n"), + Map.entry(WHITE_BISHOP, "b"), + Map.entry(WHITE_QUEEN, "q"), + Map.entry(WHITE_KING, "k"), + Map.entry(WHITE_PAWN, "p") + ); + + public String convertPieceTypeToString(final PieceType pieceType) { + return pieceFormat.get(pieceType); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000000..305982f99ee --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,50 @@ +package view; + +import domain.PieceType; +import domain.Position; +import dto.BoardDto; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class OutputView { + private static final int BOARD_SIZE = 8; + + private final OutputConvertor outputConvertor = new OutputConvertor(); + + public void printWelcomeMessage() { + System.out.println("체스 게임을 시작합니다."); + System.out.println("게임 시작은 start, 종료는 end 명령을 입력하세요."); + } + + public void printBoard(final BoardDto boardDto) { + List boardStatus = convertBoardStatus(boardDto.piecePositions()); + + boardStatus.forEach(System.out::println); + } + + private List convertBoardStatus(final Map boardStatus) { + String[][] strings = initStringsArray(); + boardStatus.forEach( + (position, pieceType) -> buildStrings(strings, position, pieceType) + ); + return Arrays.stream(strings) + .map(rowString -> String.join("", rowString)) + .toList(); + } + + private static String[][] initStringsArray() { + String[][] strings = new String[BOARD_SIZE][BOARD_SIZE]; + for (String[] row : strings) { + Arrays.fill(row, "."); + } + return strings; + } + + private void buildStrings(final String[][] strings, final Position position, final PieceType pieceType) { + int row = position.rowIndex(); + int col = position.columnIndex(); + strings[row][col] = outputConvertor.convertPieceTypeToString(pieceType); + } +} From 989bcde4113b1cf817f9ca4b6343f3dd25fe6aab Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 14:36:06 +0900 Subject: [PATCH 07/54] =?UTF-8?q?docs(readme):=202=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- docs/README.md | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 3c7873e535a..e87941958af 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,31 +1,58 @@ -# 1단계 기능 요구사항 - ## 기능 흐름 - [x] 사용자로부터 게임 시작 여부 입력 - [x] `start`를 입력받으면 체스판을 초기화한다. - [x] `end`를 입력받으면 프로그램을 종료한다. + - [ ] `move`와 출발지/목적지 정보를 입력받으면 기물 이동을 시도한다. - [x] 체스판을 초기화한다. - [x] 체스판을 출력한다. +- [ ] 체스 기물을 이동시킨다. + - [ ] 이동이 가능 여부를 확인한다. + - [ ] 기물 위치를 업데이트한다. ## 도메인별 기능 +### Vector + +- [ ] 행, 열의 두 정수 성분을 가지고, 단위벡터로서 이동의 방향성을 나타낸다. +- [ ] 출발지, 도착지의 위치 정보를 받아서 해당 방향으로 이동하기 위한 최적의 벡터 객체를 생성한다. + ### Position -- `Rank`, `File`의 쌍을 가지고 한 칸의 위치를 표현한다. +- [ ] `Rank`, `File`의 쌍을 가지고 한 칸의 위치를 표현한다. ### Piece -- 타입이 다른 기물들과 자신을 구별할 수 있는`PieceType`을 가진다. +- [ ] 타입이 다른 기물들과 자신을 구별할 수 있는`PieceType`을 가진다. +- [ ] 이동 가능한 위치인지 여부를 확인한다. +- [ ] 모든 말을 체스보드 안에서만 움직일 수 있다. +- [ ] 목적지까지 가는 경로에 다른 기물이 존재하면 이동할 수 없다. + - 록 + - [ ] 동서남북 방향으로 직진할 수 있다. + - 비숍 + - [ ] 대각선 방향으로 직진할 수 있다. + - 퀸 + - [ ] 동서남북, 혹은 대각선 방향으로 직진할 수 있다. + - 킹 + - [ ] 동서남북, 혹은 대각선 방향으로 1칸만 이동할 수 있다. + - 나이트 + - [ ] 동서남북 방향으로 한 칸 전진 후, 전진한 방향에서 대각선으로 한 칸 이동할 수 있다. + - [ ] 다른 말을 뛰어넘을 수 있다. + - 폰 + - [ ] 한 칸만 앞으로 전진만 할 수 있다. + - [ ] 다른 말을 공격하는 경우 대각선 방향으로 한 칸만 이동할 수 있다. + - [ ] 최초로 이동할 경우 한 칸 또는 두 칸 이동할 수 있다. ### Board -- 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다. +- [ ] 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다. +- [ ] 기물의 도착지에 현재 이동하는 기물과 같은 색의 기물이 존재하면 이동할 수 없다. +- [ ] 도착지에 다른 색의 기물이 존재하는 경우, 해당 기물을 제거하고 배치한다. -### view.InputView +### InputView -- `start` 명령과 `end` 명령을 입력받는다. +- [ ] `start` 명령과 `end`, `move` 명령을 입력받는다. ### OutputView -- 초기화 된 체스판을 출력한다. +- [ ] 초기화 된 체스판을 출력한다. From 71873d62a46a064f0138d6a4aba072de0a7e660a Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 14:36:38 +0900 Subject: [PATCH 08/54] =?UTF-8?q?refactor(/position):=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EC=99=80=20=EA=B4=80=EB=A0=A8=EB=90=9C=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=93=A4=EC=9D=84=20=ED=95=9C=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EB=AA=A8=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/Board.java | 2 ++ src/main/java/domain/PieceType.java | 7 +++++-- src/main/java/domain/{ => position}/File.java | 2 +- src/main/java/domain/{ => position}/Position.java | 2 +- src/main/java/domain/{ => position}/Rank.java | 2 +- src/main/java/dto/BoardDto.java | 2 +- src/main/java/dto/RequestDto.java | 4 ++-- src/main/java/view/OutputView.java | 2 +- src/test/java/domain/PositionTest.java | 3 +++ 9 files changed, 17 insertions(+), 9 deletions(-) rename src/main/java/domain/{ => position}/File.java (90%) rename src/main/java/domain/{ => position}/Position.java (89%) rename src/main/java/domain/{ => position}/Rank.java (91%) diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java index 8a4ed183cd0..cc73a643cd0 100644 --- a/src/main/java/domain/Board.java +++ b/src/main/java/domain/Board.java @@ -1,5 +1,7 @@ package domain; +import domain.position.Position; + import java.util.Arrays; import java.util.Collections; import java.util.HashMap; diff --git a/src/main/java/domain/PieceType.java b/src/main/java/domain/PieceType.java index d922b310e04..147dd4bd7fb 100644 --- a/src/main/java/domain/PieceType.java +++ b/src/main/java/domain/PieceType.java @@ -1,10 +1,13 @@ package domain; +import domain.position.File; +import domain.position.Position; + import java.util.Arrays; import java.util.List; -import static domain.File.*; -import static domain.Rank.*; +import static domain.position.File.*; +import static domain.position.Rank.*; public enum PieceType { BLACK_PAWN(Arrays.stream(File.values()).map(file -> new Position(SEVEN, file)).toList()), diff --git a/src/main/java/domain/File.java b/src/main/java/domain/position/File.java similarity index 90% rename from src/main/java/domain/File.java rename to src/main/java/domain/position/File.java index 94fbe952f5f..0343a957cd8 100644 --- a/src/main/java/domain/File.java +++ b/src/main/java/domain/position/File.java @@ -1,4 +1,4 @@ -package domain; +package domain.position; public enum File { A(0), diff --git a/src/main/java/domain/Position.java b/src/main/java/domain/position/Position.java similarity index 89% rename from src/main/java/domain/Position.java rename to src/main/java/domain/position/Position.java index 6918ba2945e..7aec38ac83c 100644 --- a/src/main/java/domain/Position.java +++ b/src/main/java/domain/position/Position.java @@ -1,4 +1,4 @@ -package domain; +package domain.position; // TODO: 캐싱 기법 고민하기 public record Position(Rank rank, File file) { diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/position/Rank.java similarity index 91% rename from src/main/java/domain/Rank.java rename to src/main/java/domain/position/Rank.java index 6e56240dac4..be75fe56b99 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/position/Rank.java @@ -1,4 +1,4 @@ -package domain; +package domain.position; public enum Rank { ONE(7), diff --git a/src/main/java/dto/BoardDto.java b/src/main/java/dto/BoardDto.java index 3d8cec6e5b2..f9c01aac752 100644 --- a/src/main/java/dto/BoardDto.java +++ b/src/main/java/dto/BoardDto.java @@ -2,7 +2,7 @@ import domain.Board; import domain.PieceType; -import domain.Position; +import domain.position.Position; import java.util.Collections; import java.util.Map; diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java index 2f1c2c77d20..9c9c43be470 100644 --- a/src/main/java/dto/RequestDto.java +++ b/src/main/java/dto/RequestDto.java @@ -1,8 +1,8 @@ package dto; -import domain.File; +import domain.position.File; import domain.GameCommand; -import domain.Rank; +import domain.position.Rank; public record RequestDto(GameCommand gameCommand, Rank rank, File file) { public static RequestDto of(GameCommand gameCommand) { diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 305982f99ee..a458fb78cff 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,7 +1,7 @@ package view; import domain.PieceType; -import domain.Position; +import domain.position.Position; import dto.BoardDto; import java.util.Arrays; diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java index c530502cdb5..1b19c3af696 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/PositionTest.java @@ -1,5 +1,8 @@ package domain; +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From f5680969e67f80971ddf61146c3b47da22fca3d5 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 17:11:55 +0900 Subject: [PATCH 09/54] =?UTF-8?q?refactor(position):=20Position=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/PieceType.java | 24 ++++++++++----------- src/main/java/domain/position/Position.java | 2 +- src/test/java/domain/PositionTest.java | 10 ++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/domain/PieceType.java b/src/main/java/domain/PieceType.java index 147dd4bd7fb..6a3bdcf1320 100644 --- a/src/main/java/domain/PieceType.java +++ b/src/main/java/domain/PieceType.java @@ -10,18 +10,18 @@ import static domain.position.Rank.*; public enum PieceType { - BLACK_PAWN(Arrays.stream(File.values()).map(file -> new Position(SEVEN, file)).toList()), - BLACK_ROOK(List.of(new Position(EIGHT, A), new Position(EIGHT, H))), - BLACK_KNIGHT(List.of(new Position(EIGHT, B), new Position(EIGHT, G))), - BLACK_BISHOP(List.of(new Position(EIGHT, C), new Position(EIGHT, F))), - BLACK_QUEEN(List.of(new Position(EIGHT, D))), - BLACK_KING(List.of(new Position(EIGHT, E))), - WHITE_PAWN(Arrays.stream(File.values()).map(file -> new Position(TWO, file)).toList()), - WHITE_ROOK(List.of(new Position(ONE, A), new Position(ONE, H))), - WHITE_KNIGHT(List.of(new Position(ONE, B), new Position(ONE, G))), - WHITE_BISHOP(List.of(new Position(ONE, C), new Position(ONE, F))), - WHITE_QUEEN(List.of(new Position(ONE, D))), - WHITE_KING(List.of(new Position(ONE, E))); + BLACK_PAWN(Arrays.stream(File.values()).map(file -> new Position(file, SEVEN)).toList()), + BLACK_ROOK(List.of(new Position(A, EIGHT), new Position(H, EIGHT))), + BLACK_KNIGHT(List.of(new Position(B, EIGHT), new Position(G, EIGHT))), + BLACK_BISHOP(List.of(new Position(C, EIGHT), new Position(F, EIGHT))), + BLACK_QUEEN(List.of(new Position(D, EIGHT))), + BLACK_KING(List.of(new Position(E, EIGHT))), + WHITE_PAWN(Arrays.stream(File.values()).map(file -> new Position(file, TWO)).toList()), + WHITE_ROOK(List.of(new Position(A, ONE), new Position(H, ONE))), + WHITE_KNIGHT(List.of(new Position(B, ONE), new Position(G, ONE))), + WHITE_BISHOP(List.of(new Position(C, ONE), new Position(F, ONE))), + WHITE_QUEEN(List.of(new Position(D, ONE))), + WHITE_KING(List.of(new Position(E, ONE))); private final List initPosition; diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java index 7aec38ac83c..42ad0d3315c 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/position/Position.java @@ -1,7 +1,7 @@ package domain.position; // TODO: 캐싱 기법 고민하기 -public record Position(Rank rank, File file) { +public record Position(File file, Rank rank) { public int rowIndex() { return rank.getIndex(); diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java index 1b19c3af696..7d4f6be5dd4 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/PositionTest.java @@ -13,14 +13,14 @@ class PositionTest { @Test void createPositionTest() { // Given - Rank row = Rank.ONE; - File col = File.A; + File file = File.A; + Rank rank = Rank.ONE; // When - Position position = new Position(row, col); + Position position = new Position(file, rank); // Then - assertThat(position.rank()).isEqualTo(row); - assertThat(position.file()).isEqualTo(col); + assertThat(position.rank()).isEqualTo(rank); + assertThat(position.file()).isEqualTo(file); } } From c08c946c1f1d8c9cab82d72d90714d91e9a46935 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 17:13:04 +0900 Subject: [PATCH 10/54] =?UTF-8?q?feat(unitVector):=208=EB=B0=A9=EC=9C=84?= =?UTF-8?q?=EB=A5=BC=20=EB=82=98=ED=83=80=EB=82=B4=EB=8A=94=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=EB=B2=A1=ED=84=B0=20=EA=B0=9D=EC=B2=B4=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 Co-authored-by: Hyunguk Ryu --- src/main/java/domain/position/UnitVector.java | 40 +++++++++++++++++++ .../java/domain/position/UnitVectorTest.java | 39 ++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/main/java/domain/position/UnitVector.java create mode 100644 src/test/java/domain/position/UnitVectorTest.java diff --git a/src/main/java/domain/position/UnitVector.java b/src/main/java/domain/position/UnitVector.java new file mode 100644 index 00000000000..d5f5701d9b3 --- /dev/null +++ b/src/main/java/domain/position/UnitVector.java @@ -0,0 +1,40 @@ +package domain.position; + +import java.util.Arrays; +import java.util.function.BiPredicate; + +public enum UnitVector { + INVALID(0, 0, (row, col) -> row == 0 && col == 0), + UP(-1, 0, (row, col) -> row < 0 && col == 0), + UP_RIGHT(-1, 1, (row, col) -> row < 0 && col > 0), + RIGHT(0, 1, (row, col) -> row == 0 && col > 0), + DOWN_RIGHT(1, 1, (row, col) -> row > 0 && col > 0), + DOWN(1, 0, (row, col) -> row > 0 && col == 0), + DOWN_LEFT(1, -1, (row, col) -> row > 0 && col < 0), + LEFT(0, -1, (row, col) -> row == 0 && col < 0), + UP_LEFT(-1, -1, (row, col) -> row < 0 && col < 0); + + private final int row; + private final int col; + private final BiPredicate condition; + + UnitVector(final int row, final int col, BiPredicate condition) { + this.row = row; + this.col = col; + this.condition = condition; + } + + public static UnitVector of(final int rowDiff, final int colDiff) { + if (isInvalid(rowDiff, colDiff)) { + return INVALID; + } + return Arrays.stream(UnitVector.values()) + .filter(unitVector -> unitVector.condition.test(rowDiff, colDiff)) + .findAny() + .orElse(INVALID); + } + + private static boolean isInvalid(int rowDiff, int colDiff) { + return (rowDiff != 0 && colDiff != 0) && (Math.abs(rowDiff) != Math.abs(colDiff)); + } +} diff --git a/src/test/java/domain/position/UnitVectorTest.java b/src/test/java/domain/position/UnitVectorTest.java new file mode 100644 index 00000000000..84bc80f7c5e --- /dev/null +++ b/src/test/java/domain/position/UnitVectorTest.java @@ -0,0 +1,39 @@ +package domain.position; + +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; + +class UnitVectorTest { + @DisplayName("출발지와 목적지의 거리차에 따라 올바른 이동 방향을 반환한다.") + @MethodSource("testSource") + @ParameterizedTest + void findUnitVectorTest(int rowDiff, int colDiff, UnitVector expected) { + // When + UnitVector unitVector = UnitVector.of(rowDiff, colDiff); + + // Then + assertThat(unitVector).isEqualTo(expected); + } + + private static Stream testSource() { + return Stream.of( + Arguments.of(0, 0, UnitVector.INVALID), + Arguments.of(-2, 0, UnitVector.UP), + Arguments.of(-7, 7, UnitVector.UP_RIGHT), + Arguments.of(0, 5, UnitVector.RIGHT), + Arguments.of(3, 3, UnitVector.DOWN_RIGHT), + Arguments.of(4, 0, UnitVector.DOWN), + Arguments.of(6, -6, UnitVector.DOWN_LEFT), + Arguments.of(0, -7, UnitVector.LEFT), + Arguments.of(-3, -3, UnitVector.UP_LEFT), + Arguments.of(2, 3, UnitVector.INVALID), + Arguments.of(-2, 1, UnitVector.INVALID) + ); + } +} From 024c0ec31dc66278dbc3f6a477d01ad1474727a3 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 17:25:33 +0900 Subject: [PATCH 11/54] =?UTF-8?q?feat(position):=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EC=97=90=EC=84=9C=20=EB=B0=B1=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=88=84=EC=A0=81=ED=95=9C=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=9C=84=EC=B9=98=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/position/File.java | 9 +++++++++ src/main/java/domain/position/Position.java | 8 +++++++- src/main/java/domain/position/Rank.java | 9 +++++++++ src/main/java/domain/position/UnitVector.java | 8 ++++++++ src/test/java/domain/PositionTest.java | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/domain/position/File.java b/src/main/java/domain/position/File.java index 0343a957cd8..5c6dc553f32 100644 --- a/src/main/java/domain/position/File.java +++ b/src/main/java/domain/position/File.java @@ -1,5 +1,7 @@ package domain.position; +import java.util.Arrays; + public enum File { A(0), B(1), @@ -19,4 +21,11 @@ public enum File { public int getIndex() { return index; } + + public static File of(final int index) { + return Arrays.stream(values()) + .filter(file -> file.getIndex() == index) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 인덱스입니다.")); + } } diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java index 42ad0d3315c..f3b9c9affe6 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/position/Position.java @@ -2,7 +2,6 @@ // TODO: 캐싱 기법 고민하기 public record Position(File file, Rank rank) { - public int rowIndex() { return rank.getIndex(); } @@ -10,4 +9,11 @@ public int rowIndex() { public int columnIndex() { return file.getIndex(); } + + public Position add(final UnitVector unitVector) { + File newFile = File.of(columnIndex() + unitVector.getCol()); + Rank newRank = Rank.of(rowIndex() + unitVector.getRow()); + + return new Position(newFile, newRank); + } } diff --git a/src/main/java/domain/position/Rank.java b/src/main/java/domain/position/Rank.java index be75fe56b99..8e1258b126e 100644 --- a/src/main/java/domain/position/Rank.java +++ b/src/main/java/domain/position/Rank.java @@ -1,5 +1,7 @@ package domain.position; +import java.util.Arrays; + public enum Rank { ONE(7), TWO(6), @@ -19,4 +21,11 @@ public enum Rank { public int getIndex() { return index; } + + public static Rank of(final int index) { + return Arrays.stream(Rank.values()) + .filter(rank -> rank.getIndex() == index) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 인덱스입니다.")); + } } diff --git a/src/main/java/domain/position/UnitVector.java b/src/main/java/domain/position/UnitVector.java index d5f5701d9b3..015ff8c49e7 100644 --- a/src/main/java/domain/position/UnitVector.java +++ b/src/main/java/domain/position/UnitVector.java @@ -37,4 +37,12 @@ public static UnitVector of(final int rowDiff, final int colDiff) { private static boolean isInvalid(int rowDiff, int colDiff) { return (rowDiff != 0 && colDiff != 0) && (Math.abs(rowDiff) != Math.abs(colDiff)); } + + public int getRow() { + return row; + } + + public int getCol() { + return col; + } } diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java index 7d4f6be5dd4..075bb711555 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/PositionTest.java @@ -3,6 +3,7 @@ import domain.position.File; import domain.position.Position; import domain.position.Rank; +import domain.position.UnitVector; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,4 +24,18 @@ void createPositionTest() { assertThat(position.rank()).isEqualTo(rank); assertThat(position.file()).isEqualTo(file); } + + @DisplayName("백터 값을 넘기면 새로운 위치의 Position을 반환한다.") + @Test + void addPositionTest() { + // Given + Position position = new Position(File.D, Rank.TWO); + + // When + Position newPosition = position.add(UnitVector.UP_RIGHT); + + // Then + assertThat(newPosition.file()).isEqualTo(File.E); + assertThat(newPosition.rank()).isEqualTo(Rank.THREE); + } } From 2c979120ea2fdb6ace7034a252227718af5ac441 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 20:13:35 +0900 Subject: [PATCH 12/54] =?UTF-8?q?feat(orthogonalMoveStrategy):=20=EC=A7=81?= =?UTF-8?q?=EC=84=A0=20=EB=B0=A9=ED=96=A5=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=EB=A5=BC=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EC=A0=84=EB=9E=B5=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 Co-authored-by: Hyunguk Ryu --- .../java/domain/strategy/MoveStrategy.java | 10 +++ .../strategy/OrthogonalMoveStrategy.java | 42 ++++++++++++ .../strategy/OrthogonalMoveStrategyTest.java | 64 +++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 src/main/java/domain/strategy/MoveStrategy.java create mode 100644 src/main/java/domain/strategy/OrthogonalMoveStrategy.java create mode 100644 src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java diff --git a/src/main/java/domain/strategy/MoveStrategy.java b/src/main/java/domain/strategy/MoveStrategy.java new file mode 100644 index 00000000000..104c9ff2578 --- /dev/null +++ b/src/main/java/domain/strategy/MoveStrategy.java @@ -0,0 +1,10 @@ +package domain.strategy; + +import domain.position.Position; + +import java.util.Set; + +@FunctionalInterface +public interface MoveStrategy { + boolean isMovable(Position source, Position destination, Set otherPiecesPosition); +} diff --git a/src/main/java/domain/strategy/OrthogonalMoveStrategy.java b/src/main/java/domain/strategy/OrthogonalMoveStrategy.java new file mode 100644 index 00000000000..0dee78f8f92 --- /dev/null +++ b/src/main/java/domain/strategy/OrthogonalMoveStrategy.java @@ -0,0 +1,42 @@ +package domain.strategy; + +import domain.position.Position; +import domain.position.UnitVector; + +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static domain.position.UnitVector.*; + +public class OrthogonalMoveStrategy implements ContinuousMoveStrategy { + private static final Set acceptableVectors = Set.of(UP, RIGHT, DOWN, LEFT); + + @Override + public boolean isMovable(final Position source, final Position destination, final Set otherPiecesPosition) { + UnitVector optimalVector = findOptimalVector(source, destination); + + if (!acceptableVectors.contains(optimalVector)) { + return false; + } + + List movePaths = Stream.iterate(source, position -> position.add(optimalVector)) + .takeWhile(position -> !position.equals(destination) && !otherPiecesPosition.contains(position)) + .limit(8) + .toList(); + + return isReachable(destination, optimalVector, movePaths); + } + + private boolean isReachable(final Position destination, final UnitVector optimalVector, final List movePaths) { + Position finalPosition = movePaths.get(movePaths.size() - 1).add(optimalVector); + return finalPosition.equals(destination); + } + + private UnitVector findOptimalVector(final Position source, final Position destination) { + int rowDiff = destination.rowIndex() - source.rowIndex(); + int colDiff = destination.columnIndex() - source.columnIndex(); + + return UnitVector.of(rowDiff, colDiff); + } +} diff --git a/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java b/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java new file mode 100644 index 00000000000..aae956fbab3 --- /dev/null +++ b/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java @@ -0,0 +1,64 @@ +package domain.strategy; + +import domain.position.Position; +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.Collections; +import java.util.Set; +import java.util.stream.Stream; + +import static domain.position.File.*; +import static domain.position.Rank.*; +import static org.assertj.core.api.Assertions.assertThat; + +class OrthogonalMoveStrategyTest { + @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 가능한 경우 true를 반환한다.") + @MethodSource("validMove") + @ParameterizedTest + void isMovableTest(Position source, Position destination) { + // Given + OrthogonalMoveStrategy orthogonalMoveStrategy = new OrthogonalMoveStrategy(); + Set others = Collections.emptySet(); + + // When + boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isTrue(); + } + + private static Stream validMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(B, SEVEN)), + Arguments.of(new Position(D, THREE), new Position(F, THREE)), + Arguments.of(new Position(D, FIVE), new Position(D, TWO)), + Arguments.of(new Position(D, FIVE), new Position(A, FIVE)) + ); + } + + @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 불가능한 경우 false를 반환한다.") + @MethodSource("invalidMove") + @ParameterizedTest + void isNotMovableTest(Position source, Position destination, Set others) { + // Given + OrthogonalMoveStrategy orthogonalMoveStrategy = new OrthogonalMoveStrategy(); + + // When + boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isFalse(); + } + + private static Stream invalidMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(B, SEVEN), Set.of(new Position(B, FIVE))), + Arguments.of(new Position(D, THREE), new Position(F, THREE), Set.of(new Position(E, THREE))), + Arguments.of(new Position(D, FIVE), new Position(D, TWO), Set.of(new Position(D, THREE))), + Arguments.of(new Position(D, FIVE), new Position(A, FIVE), Set.of(new Position(C, FIVE))) + ); + } +} From 2474fb9d0d7376b499035f0d75f309bb0393cec8 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Wed, 20 Mar 2024 21:16:33 +0900 Subject: [PATCH 13/54] =?UTF-8?q?feat(continuousMoveStrategy):=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B0=80=EB=8A=A5=ED=95=9C=20=EB=B0=A9=ED=96=A5?= =?UTF-8?q?=EB=B2=A1=ED=84=B0=EC=99=80=20=EC=9D=B4=EB=8F=99=20=ED=9A=9F?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=ED=95=9C=EC=9D=84=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EB=8A=94=20=EC=97=B0=EC=86=8D=20=EC=9D=B4=EB=8F=99=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- ...ategy.java => ContinuousMoveStrategy.java} | 12 +- .../strategy/ContinuousMoveStrategyTest.java | 139 ++++++++++++++++++ .../strategy/OrthogonalMoveStrategyTest.java | 64 -------- 3 files changed, 147 insertions(+), 68 deletions(-) rename src/main/java/domain/strategy/{OrthogonalMoveStrategy.java => ContinuousMoveStrategy.java} (78%) create mode 100644 src/test/java/domain/strategy/ContinuousMoveStrategyTest.java delete mode 100644 src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java diff --git a/src/main/java/domain/strategy/OrthogonalMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java similarity index 78% rename from src/main/java/domain/strategy/OrthogonalMoveStrategy.java rename to src/main/java/domain/strategy/ContinuousMoveStrategy.java index 0dee78f8f92..6f528ca4e36 100644 --- a/src/main/java/domain/strategy/OrthogonalMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -7,10 +7,14 @@ import java.util.Set; import java.util.stream.Stream; -import static domain.position.UnitVector.*; +public class ContinuousMoveStrategy implements MoveStrategy { + private final Set acceptableVectors; + private final int moveBound; -public class OrthogonalMoveStrategy implements ContinuousMoveStrategy { - private static final Set acceptableVectors = Set.of(UP, RIGHT, DOWN, LEFT); + public ContinuousMoveStrategy(Set acceptableVectors, int moveBound) { + this.acceptableVectors = acceptableVectors; + this.moveBound = moveBound; + } @Override public boolean isMovable(final Position source, final Position destination, final Set otherPiecesPosition) { @@ -22,7 +26,7 @@ public boolean isMovable(final Position source, final Position destination, fina List movePaths = Stream.iterate(source, position -> position.add(optimalVector)) .takeWhile(position -> !position.equals(destination) && !otherPiecesPosition.contains(position)) - .limit(8) + .limit(moveBound) .toList(); return isReachable(destination, optimalVector, movePaths); diff --git a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java new file mode 100644 index 00000000000..4e380aeb53d --- /dev/null +++ b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java @@ -0,0 +1,139 @@ +package domain.strategy; + +import domain.position.Position; +import domain.position.UnitVector; +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.Collections; +import java.util.Set; +import java.util.stream.Stream; + +import static domain.position.File.*; +import static domain.position.Rank.*; +import static domain.position.UnitVector.*; +import static org.assertj.core.api.Assertions.assertThat; + +class ContinuousMoveStrategyTest { + private static final Set orthogonalVectors = Set.of(UP, RIGHT, DOWN, LEFT); + private static final Set diagonalVectors = Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + private static final Set omnidirectionalVectors = Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + private static final ContinuousMoveStrategy orthogonalMoveStrategy = new ContinuousMoveStrategy(orthogonalVectors, 8); + private static final ContinuousMoveStrategy diagonalMoveStrategy = new ContinuousMoveStrategy(diagonalVectors, 8); + + @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 가능한 경우 true를 반환한다.") + @MethodSource("orthogonalValidMove") + @ParameterizedTest + void orthogonalMovableTest(Position source, Position destination) { + // Given + Set others = Collections.emptySet(); + + // When + boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isTrue(); + } + + private static Stream orthogonalValidMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(B, SEVEN)), + Arguments.of(new Position(D, THREE), new Position(F, THREE)), + Arguments.of(new Position(D, FIVE), new Position(D, TWO)), + Arguments.of(new Position(D, FIVE), new Position(A, FIVE)) + ); + } + + @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 불가능한 경우 false를 반환한다.") + @MethodSource("orthogonalInvalidMove") + @ParameterizedTest + void notOrthogonalMovableTest(Position source, Position destination, Set others) { + // When + boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isFalse(); + } + + private static Stream orthogonalInvalidMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(B, SEVEN), Set.of(new Position(B, FIVE))), + Arguments.of(new Position(D, THREE), new Position(F, THREE), Set.of(new Position(E, THREE))), + Arguments.of(new Position(D, FIVE), new Position(D, TWO), Set.of(new Position(D, THREE))), + Arguments.of(new Position(D, FIVE), new Position(A, FIVE), Set.of(new Position(C, FIVE))), + Arguments.of(new Position(D, FIVE), new Position(B, FOUR), Collections.emptySet()), + Arguments.of(new Position(D, FIVE), new Position(B, THREE), Collections.emptySet()) + ); + } + + @DisplayName("출발지에서 목적지까지 대각선으로 이동 가능한 경우 true를 반환한다.") + @MethodSource("diagonalValidMove") + @ParameterizedTest + void diagonalMovableTest(Position source, Position destination) { + // Given + Set others = Collections.emptySet(); + + // When + boolean movable = diagonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isTrue(); + } + + private static Stream diagonalValidMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(F, SEVEN)), + Arguments.of(new Position(D, THREE), new Position(F, ONE)), + Arguments.of(new Position(D, FIVE), new Position(A, TWO)), + Arguments.of(new Position(D, FIVE), new Position(C, SIX)) + ); + } + + @DisplayName("출발지에서 목적지까지 대각선으로 이동 불가능한 경우 false를 반환한다.") + @MethodSource("diagonalInvalidMove") + @ParameterizedTest + void notDiagonalMovableTest(Position source, Position destination, Set others) { + // When + boolean movable = diagonalMoveStrategy.isMovable(source, destination, others); + + // Then + assertThat(movable).isFalse(); + } + + private static Stream diagonalInvalidMove() { + return Stream.of( + Arguments.of(new Position(B, THREE), new Position(F, SEVEN), Set.of(new Position(D, FIVE))), + Arguments.of(new Position(D, THREE), new Position(F, ONE), Set.of(new Position(E, TWO))), + Arguments.of(new Position(D, FIVE), new Position(A, TWO), Set.of(new Position(B, THREE))), + Arguments.of(new Position(D, FIVE), new Position(B, SEVEN), Set.of(new Position(C, SIX))), + Arguments.of(new Position(D, FIVE), new Position(H, FIVE), Collections.emptySet()), + Arguments.of(new Position(D, FIVE), new Position(F, EIGHT), Collections.emptySet()) + ); + } + + + @DisplayName("출발지에서 목적지까지 정해진 거리 이내에 이동이 불가능한 경우 false를 반환한다.") + @MethodSource("boundedMove") + @ParameterizedTest + void notDiagonalMovableTest(Position source, Position destination, int moveBound) { + // When + ContinuousMoveStrategy limitedBoundMoveStrategy = new ContinuousMoveStrategy(omnidirectionalVectors, moveBound); + boolean movable = limitedBoundMoveStrategy.isMovable(source, destination, Collections.emptySet()); + + // Then + assertThat(movable).isFalse(); + } + + private static Stream boundedMove() { + return Stream.of( + Arguments.of(new Position(D, FIVE), new Position(F, SEVEN), 1), + Arguments.of(new Position(D, FIVE), new Position(G, EIGHT), 2), + Arguments.of(new Position(A, EIGHT), new Position(E, FOUR), 3), + Arguments.of(new Position(A, EIGHT), new Position(A, THREE), 4), + Arguments.of(new Position(A, EIGHT), new Position(H, ONE), 5), + Arguments.of(new Position(A, EIGHT), new Position(H, EIGHT), 6) + ); + } +} diff --git a/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java b/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java deleted file mode 100644 index aae956fbab3..00000000000 --- a/src/test/java/domain/strategy/OrthogonalMoveStrategyTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package domain.strategy; - -import domain.position.Position; -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.Collections; -import java.util.Set; -import java.util.stream.Stream; - -import static domain.position.File.*; -import static domain.position.Rank.*; -import static org.assertj.core.api.Assertions.assertThat; - -class OrthogonalMoveStrategyTest { - @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 가능한 경우 true를 반환한다.") - @MethodSource("validMove") - @ParameterizedTest - void isMovableTest(Position source, Position destination) { - // Given - OrthogonalMoveStrategy orthogonalMoveStrategy = new OrthogonalMoveStrategy(); - Set others = Collections.emptySet(); - - // When - boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isTrue(); - } - - private static Stream validMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(B, SEVEN)), - Arguments.of(new Position(D, THREE), new Position(F, THREE)), - Arguments.of(new Position(D, FIVE), new Position(D, TWO)), - Arguments.of(new Position(D, FIVE), new Position(A, FIVE)) - ); - } - - @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 불가능한 경우 false를 반환한다.") - @MethodSource("invalidMove") - @ParameterizedTest - void isNotMovableTest(Position source, Position destination, Set others) { - // Given - OrthogonalMoveStrategy orthogonalMoveStrategy = new OrthogonalMoveStrategy(); - - // When - boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isFalse(); - } - - private static Stream invalidMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(B, SEVEN), Set.of(new Position(B, FIVE))), - Arguments.of(new Position(D, THREE), new Position(F, THREE), Set.of(new Position(E, THREE))), - Arguments.of(new Position(D, FIVE), new Position(D, TWO), Set.of(new Position(D, THREE))), - Arguments.of(new Position(D, FIVE), new Position(A, FIVE), Set.of(new Position(C, FIVE))) - ); - } -} From 244b111119c8dd58be1745addca7f486ca8d6887 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 00:40:37 +0900 Subject: [PATCH 14/54] =?UTF-8?q?refactor(boardInitializer):=EC=B2=B4?= =?UTF-8?q?=EC=8A=A4=ED=8C=90=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/controller/ChessController.java | 3 +- src/main/java/domain/Board.java | 11 --- src/main/java/domain/BoardInitializer.java | 67 +++++++++++++++++++ src/main/java/domain/position/UnitVector.java | 13 ++++ src/test/java/domain/BoardTest.java | 2 +- 5 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 src/main/java/domain/BoardInitializer.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index c33a4073259..18df5c407dd 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,6 +1,7 @@ package controller; import domain.Board; +import domain.BoardInitializer; import domain.GameCommand; import dto.BoardDto; import dto.RequestDto; @@ -28,7 +29,7 @@ public void run() { } private void startGame() { - Board board = Board.init(); + Board board = BoardInitializer.init(); BoardDto boardDto = BoardDto.from(board); diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java index cc73a643cd0..cf24a9ca3ba 100644 --- a/src/main/java/domain/Board.java +++ b/src/main/java/domain/Board.java @@ -2,9 +2,7 @@ import domain.position.Position; -import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.Map; public class Board { @@ -14,15 +12,6 @@ public Board(final Map chessBoard) { this.chessBoard = chessBoard; } - public static Board init() { - Map initialPositions = new HashMap<>(); - Arrays.stream(PieceType.values()) - .forEach(pieceType -> pieceType.getInitPosition() - .forEach(position -> initialPositions.put(position, new Piece(pieceType)))); - - return new Board(initialPositions); - } - public Map getChessBoard() { return Collections.unmodifiableMap(chessBoard); } diff --git a/src/main/java/domain/BoardInitializer.java b/src/main/java/domain/BoardInitializer.java new file mode 100644 index 00000000000..479b024de98 --- /dev/null +++ b/src/main/java/domain/BoardInitializer.java @@ -0,0 +1,67 @@ +package domain; + +import domain.position.File; +import domain.position.Position; +import domain.position.UnitVector; +import domain.strategy.ContinuousMoveStrategy; +import domain.strategy.KnightMoveStrategy; +import domain.strategy.MoveStrategy; +import domain.strategy.PawnMoveStrategy; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static domain.PieceType.*; +import static domain.position.File.*; +import static domain.position.Rank.*; + +public class BoardInitializer { + private static final int MAXIMUM_MOVE_BOUND = 8; + private static final int KING_MAXIMUM_MOVE_BOUND = 1; + + private static final MoveStrategy pawnStrategy = new PawnMoveStrategy(); + private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); + private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(UnitVector.orthogonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(UnitVector.diagonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); + private static final Map> pieceInitialPositions = Map.ofEntries( + Map.entry(BLACK_PAWN, Arrays.stream(File.values()).map(file -> new Position(file, SEVEN)).toList()), + Map.entry(BLACK_ROOK, List.of(new Position(A, EIGHT), new Position(H, EIGHT))), + Map.entry(BLACK_KNIGHT, List.of(new Position(B, EIGHT), new Position(G, EIGHT))), + Map.entry(BLACK_BISHOP, List.of(new Position(C, EIGHT), new Position(F, EIGHT))), + Map.entry(BLACK_QUEEN, List.of(new Position(D, EIGHT))), + Map.entry(BLACK_KING, List.of(new Position(E, EIGHT))), + Map.entry(WHITE_PAWN, Arrays.stream(File.values()).map(file -> new Position(file, TWO)).toList()), + Map.entry(WHITE_ROOK, List.of(new Position(A, ONE), new Position(H, ONE))), + Map.entry(WHITE_KNIGHT, List.of(new Position(B, ONE), new Position(G, ONE))), + Map.entry(WHITE_BISHOP, List.of(new Position(C, ONE), new Position(F, ONE))), + Map.entry(WHITE_QUEEN, List.of(new Position(D, ONE))), + Map.entry(WHITE_KING, List.of(new Position(E, ONE))) + ); + private static final Map moveStrategies = Map.ofEntries( + Map.entry(BLACK_PAWN, pawnStrategy), + Map.entry(BLACK_ROOK, rookStrategy), + Map.entry(BLACK_KNIGHT, knightStrategy), + Map.entry(BLACK_BISHOP, bishopStrategy), + Map.entry(BLACK_QUEEN, queenStrategy), + Map.entry(BLACK_KING, kingStrategy), + Map.entry(WHITE_PAWN, pawnStrategy), + Map.entry(WHITE_ROOK, rookStrategy), + Map.entry(WHITE_KNIGHT, knightStrategy), + Map.entry(WHITE_BISHOP, bishopStrategy), + Map.entry(WHITE_QUEEN, queenStrategy), + Map.entry(WHITE_KING, kingStrategy) + ); + + public static Board init() { + Map positions = new HashMap<>(); + pieceInitialPositions.forEach(((pieceType, position) -> { + position.forEach(p -> positions.put(p, new Piece(pieceType, moveStrategies.get(pieceType)))); + })); + + return new Board(positions); + } +} diff --git a/src/main/java/domain/position/UnitVector.java b/src/main/java/domain/position/UnitVector.java index 015ff8c49e7..47e0f51390e 100644 --- a/src/main/java/domain/position/UnitVector.java +++ b/src/main/java/domain/position/UnitVector.java @@ -1,6 +1,7 @@ package domain.position; import java.util.Arrays; +import java.util.Set; import java.util.function.BiPredicate; public enum UnitVector { @@ -38,6 +39,18 @@ private static boolean isInvalid(int rowDiff, int colDiff) { return (rowDiff != 0 && colDiff != 0) && (Math.abs(rowDiff) != Math.abs(colDiff)); } + public static Set orthogonalVectors() { + return Set.of(UP, RIGHT, DOWN, LEFT); + } + + public static Set diagonalVectors() { + return Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + } + + public static Set omnidirectionalVectors() { + return Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + } + public int getRow() { return row; } diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java index 0b82efb3e4b..9e4a8ca1962 100644 --- a/src/test/java/domain/BoardTest.java +++ b/src/test/java/domain/BoardTest.java @@ -11,7 +11,7 @@ class BoardTest { @Test void createBoard() { // When - Board board = Board.init(); + Board board = BoardInitializer.init(); // Then assertThat(board).isNotNull(); From 76b901fb43613cd653a6fe3745482d833830ca97 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 03:23:01 +0900 Subject: [PATCH 15/54] =?UTF-8?q?refactor(continuousMoveStrategy):=20?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A6=BC=20=EB=82=B4=EC=9D=98=20Predicate=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- .../java/domain/strategy/ContinuousMoveStrategy.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java index 6f528ca4e36..07703a40cb4 100644 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -17,7 +17,7 @@ public ContinuousMoveStrategy(Set acceptableVectors, int moveBound) } @Override - public boolean isMovable(final Position source, final Position destination, final Set otherPiecesPosition) { + public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { UnitVector optimalVector = findOptimalVector(source, destination); if (!acceptableVectors.contains(optimalVector)) { @@ -25,13 +25,20 @@ public boolean isMovable(final Position source, final Position destination, fina } List movePaths = Stream.iterate(source, position -> position.add(optimalVector)) - .takeWhile(position -> !position.equals(destination) && !otherPiecesPosition.contains(position)) + .takeWhile(position -> isAlive(position, destination, piecePositions)) .limit(moveBound) .toList(); return isReachable(destination, optimalVector, movePaths); } + // TODO: 메서드 이름 짓기 + private static boolean isAlive(final Position current, final Position destination, final Set piecePositions) { + boolean isReachedDestination = current.equals(destination); + boolean isOtherPieceExist = piecePositions.contains(current); + return !isReachedDestination && !isOtherPieceExist; + } + private boolean isReachable(final Position destination, final UnitVector optimalVector, final List movePaths) { Position finalPosition = movePaths.get(movePaths.size() - 1).add(optimalVector); return finalPosition.equals(destination); From d5a7cc62ab346d6513eb98e8653c481e2f688df3 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 03:23:56 +0900 Subject: [PATCH 16/54] =?UTF-8?q?feat(board):=20=EC=B2=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=A7=90=EC=9D=84=20=EC=9D=B4=EB=8F=99=EC=8B=9C=ED=82=A4?= =?UTF-8?q?=EB=8A=94=20=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 Co-authored-by: Hyunguk Ryu --- src/main/java/domain/Board.java | 37 ++++++++++++++++++++++++ src/main/java/domain/Piece.java | 17 ++++++++++- src/main/java/domain/PieceType.java | 44 +++++++++++++---------------- src/main/java/domain/TeamColor.java | 5 ++++ 4 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 src/main/java/domain/TeamColor.java diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java index cf24a9ca3ba..4b01bdf0321 100644 --- a/src/main/java/domain/Board.java +++ b/src/main/java/domain/Board.java @@ -3,7 +3,9 @@ import domain.position.Position; import java.util.Collections; +import java.util.HashSet; import java.util.Map; +import java.util.Set; public class Board { private final Map chessBoard; @@ -15,4 +17,39 @@ public Board(final Map chessBoard) { public Map getChessBoard() { return Collections.unmodifiableMap(chessBoard); } + + public void movePiece(final TeamColor teamColor, final Position source, final Position destination) { + validateRequest(teamColor, source); + + Piece piece = chessBoard.get(source); + if (!piece.isMovable(source, destination, parseOtherPiecePositions(source))) { + throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + } + + // 이동 위치에 아군 기물이 존재 + if (isPieceExist(destination)) { + if (chessBoard.get(destination).hasColor(teamColor)) { + throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + } + } + // 공격 없이 빈 칸으로 이동하는 경우 + chessBoard.put(destination, piece); + chessBoard.remove(source); + } + + private Set parseOtherPiecePositions(final Position source) { + Set positions = new HashSet<>(chessBoard.keySet()); + positions.remove(source); + return positions; + } + + private void validateRequest(final TeamColor teamColor, final Position source) { + if (!chessBoard.containsKey(source) || !chessBoard.get(source).hasColor(teamColor)) { + throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + } + } + + private boolean isPieceExist(Position position) { + return chessBoard.containsKey(position); + } } diff --git a/src/main/java/domain/Piece.java b/src/main/java/domain/Piece.java index aeb7b810868..aab5f66be86 100644 --- a/src/main/java/domain/Piece.java +++ b/src/main/java/domain/Piece.java @@ -1,13 +1,28 @@ package domain; +import domain.position.Position; +import domain.strategy.MoveStrategy; + +import java.util.Set; + public class Piece { private final PieceType pieceType; + private final MoveStrategy moveStrategy; - public Piece(final PieceType pieceType) { + public Piece(final PieceType pieceType, final MoveStrategy moveStrategy) { this.pieceType = pieceType; + this.moveStrategy = moveStrategy; } public PieceType getPieceType() { return pieceType; } + + public boolean hasColor(final TeamColor teamColor) { + return pieceType.hasColor(teamColor); + } + + public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { + return moveStrategy.isMovable(source, destination, piecePositions); + } } diff --git a/src/main/java/domain/PieceType.java b/src/main/java/domain/PieceType.java index 6a3bdcf1320..b8a7ea74aab 100644 --- a/src/main/java/domain/PieceType.java +++ b/src/main/java/domain/PieceType.java @@ -1,35 +1,29 @@ package domain; -import domain.position.File; -import domain.position.Position; - -import java.util.Arrays; -import java.util.List; - -import static domain.position.File.*; -import static domain.position.Rank.*; +import static domain.TeamColor.BLACK; +import static domain.TeamColor.WHITE; public enum PieceType { - BLACK_PAWN(Arrays.stream(File.values()).map(file -> new Position(file, SEVEN)).toList()), - BLACK_ROOK(List.of(new Position(A, EIGHT), new Position(H, EIGHT))), - BLACK_KNIGHT(List.of(new Position(B, EIGHT), new Position(G, EIGHT))), - BLACK_BISHOP(List.of(new Position(C, EIGHT), new Position(F, EIGHT))), - BLACK_QUEEN(List.of(new Position(D, EIGHT))), - BLACK_KING(List.of(new Position(E, EIGHT))), - WHITE_PAWN(Arrays.stream(File.values()).map(file -> new Position(file, TWO)).toList()), - WHITE_ROOK(List.of(new Position(A, ONE), new Position(H, ONE))), - WHITE_KNIGHT(List.of(new Position(B, ONE), new Position(G, ONE))), - WHITE_BISHOP(List.of(new Position(C, ONE), new Position(F, ONE))), - WHITE_QUEEN(List.of(new Position(D, ONE))), - WHITE_KING(List.of(new Position(E, ONE))); + BLACK_PAWN(BLACK), + BLACK_ROOK(BLACK), + BLACK_KNIGHT(BLACK), + BLACK_BISHOP(BLACK), + BLACK_QUEEN(BLACK), + BLACK_KING(BLACK), + WHITE_PAWN(WHITE), + WHITE_ROOK(WHITE), + WHITE_KNIGHT(WHITE), + WHITE_BISHOP(WHITE), + WHITE_QUEEN(WHITE), + WHITE_KING(WHITE); - private final List initPosition; + private final TeamColor teamColor; - PieceType(final List initPosition) { - this.initPosition = initPosition; + PieceType(final TeamColor teamColor) { + this.teamColor = teamColor; } - public List getInitPosition() { - return initPosition; + public boolean hasColor(TeamColor teamColor) { + return this.teamColor.equals(teamColor); } } diff --git a/src/main/java/domain/TeamColor.java b/src/main/java/domain/TeamColor.java new file mode 100644 index 00000000000..16416b5fadc --- /dev/null +++ b/src/main/java/domain/TeamColor.java @@ -0,0 +1,5 @@ +package domain; + +public enum TeamColor { + BLACK, WHITE +} From eab0b5cc963da8392c4a69c2bf96e7205eb104dc Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 03:24:36 +0900 Subject: [PATCH 17/54] =?UTF-8?q?feat(knightMoveStrategy):=20=EB=82=98?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=9D=B4=EB=8F=99=20=EC=A0=84=EB=9E=B5=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 Co-authored-by: Hyunguk Ryu --- .../java/domain/position/KnightDirection.java | 36 +++++++++++++++++++ .../domain/strategy/KnightMoveStrategy.java | 16 +++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/main/java/domain/position/KnightDirection.java create mode 100644 src/main/java/domain/strategy/KnightMoveStrategy.java diff --git a/src/main/java/domain/position/KnightDirection.java b/src/main/java/domain/position/KnightDirection.java new file mode 100644 index 00000000000..3726f5a8fe1 --- /dev/null +++ b/src/main/java/domain/position/KnightDirection.java @@ -0,0 +1,36 @@ +package domain.position; + +import java.util.Arrays; +import java.util.List; + +public enum KnightDirection { + UP_RIGHT(-2, 1), + RIGHT_UP(-1, 2), + RIGHT_DOWN(1, 2), + DOWN_RIGHT(2, 1), + DOWN_LEFT(2, -1), + LEFT_DOWN(1, -2), + LEFT_UP(-1, -2), + UP_LEFT(-2, -1); + + private final int row; + private final int col; + + KnightDirection(final int row, final int col) { + this.row = row; + this.col = col; + } + + private boolean hasPosition(int row, int col) { + return this.row == row && this.col == col; + } + + public static List asList() { + return Arrays.stream(values()).toList(); + } + + public static boolean isExist(int row, int col) { + return Arrays.stream(values()) + .anyMatch(knightDirection -> knightDirection.hasPosition(row, col)); + } +} diff --git a/src/main/java/domain/strategy/KnightMoveStrategy.java b/src/main/java/domain/strategy/KnightMoveStrategy.java new file mode 100644 index 00000000000..58a853256a8 --- /dev/null +++ b/src/main/java/domain/strategy/KnightMoveStrategy.java @@ -0,0 +1,16 @@ +package domain.strategy; + +import domain.position.KnightDirection; +import domain.position.Position; + +import java.util.Set; + +public class KnightMoveStrategy implements MoveStrategy { + @Override + public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { + int rowDiff = destination.rowIndex() - source.rowIndex(); + int colDiff = destination.columnIndex() - source.columnIndex(); + + return KnightDirection.isExist(rowDiff, colDiff); + } +} From b08219b8d0e8bf44f04e924b8c3548e0e6fdfb93 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 03:25:14 +0900 Subject: [PATCH 18/54] =?UTF-8?q?feat(pawnMoveStrategy):=20=ED=8F=B0?= =?UTF-8?q?=EC=9D=98=20=EC=9D=B4=EB=8F=99=20=EC=A0=84=EB=9E=B5=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 Co-authored-by: Hyunguk Ryu --- src/main/java/domain/BoardInitializer.java | 7 +- .../domain/strategy/PawnMoveStrategy.java | 70 +++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 src/main/java/domain/strategy/PawnMoveStrategy.java diff --git a/src/main/java/domain/BoardInitializer.java b/src/main/java/domain/BoardInitializer.java index 479b024de98..15c363be81c 100644 --- a/src/main/java/domain/BoardInitializer.java +++ b/src/main/java/domain/BoardInitializer.java @@ -21,7 +21,8 @@ public class BoardInitializer { private static final int MAXIMUM_MOVE_BOUND = 8; private static final int KING_MAXIMUM_MOVE_BOUND = 1; - private static final MoveStrategy pawnStrategy = new PawnMoveStrategy(); + private static final MoveStrategy whitePawnStrategy = new PawnMoveStrategy(TeamColor.WHITE); + private static final MoveStrategy blackPawnStrategy = new PawnMoveStrategy(TeamColor.BLACK); private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(UnitVector.orthogonalVectors(), MAXIMUM_MOVE_BOUND); private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(UnitVector.diagonalVectors(), MAXIMUM_MOVE_BOUND); @@ -42,13 +43,13 @@ public class BoardInitializer { Map.entry(WHITE_KING, List.of(new Position(E, ONE))) ); private static final Map moveStrategies = Map.ofEntries( - Map.entry(BLACK_PAWN, pawnStrategy), + Map.entry(BLACK_PAWN, blackPawnStrategy), Map.entry(BLACK_ROOK, rookStrategy), Map.entry(BLACK_KNIGHT, knightStrategy), Map.entry(BLACK_BISHOP, bishopStrategy), Map.entry(BLACK_QUEEN, queenStrategy), Map.entry(BLACK_KING, kingStrategy), - Map.entry(WHITE_PAWN, pawnStrategy), + Map.entry(WHITE_PAWN, whitePawnStrategy), Map.entry(WHITE_ROOK, rookStrategy), Map.entry(WHITE_KNIGHT, knightStrategy), Map.entry(WHITE_BISHOP, bishopStrategy), diff --git a/src/main/java/domain/strategy/PawnMoveStrategy.java b/src/main/java/domain/strategy/PawnMoveStrategy.java new file mode 100644 index 00000000000..392f427a109 --- /dev/null +++ b/src/main/java/domain/strategy/PawnMoveStrategy.java @@ -0,0 +1,70 @@ +package domain.strategy; + +import domain.TeamColor; +import domain.position.Position; +import domain.position.Rank; + +import java.util.Set; + +public class PawnMoveStrategy implements MoveStrategy { + private final TeamColor teamColor; + + public PawnMoveStrategy(final TeamColor teamColor) { + this.teamColor = teamColor; + } + + @Override + public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { + if (teamColor == TeamColor.WHITE) { + return isWhiteMovable(source, destination, piecePositions.contains(destination)); + } + return isBlackMovable(source, destination, piecePositions.contains(destination)); + } + + private boolean isWhiteMovable(final Position source, final Position destination, boolean isExistOtherPiece) { + int rowDiff = destination.rowIndex() - source.rowIndex(); + int colDiff = destination.columnIndex() - source.columnIndex(); + + return goUpWhite(rowDiff, colDiff) || goUpUpWhite(rowDiff, colDiff, source) || goCrossWhite(rowDiff, colDiff, isExistOtherPiece); + } + + private boolean goCrossWhite(final int rowDiff, final int colDiff, final boolean isExistOtherPiece) { + return rowDiff == -1 && Math.abs(colDiff) == 1 && isExistOtherPiece; + } + + private boolean goUpWhite(int rowDiff, int colDiff) { + return rowDiff == -1 && colDiff == 0; + } + + private boolean goUpUpWhite(int rowDiff, int colDiff, Position source) { + return rowDiff == -2 && colDiff == 0 && isInitialPosition(source); + } + + + private boolean isBlackMovable(final Position source, final Position destination, final boolean isExistOtherPiece) { + int rowDiff = destination.rowIndex() - source.rowIndex(); + int colDiff = destination.columnIndex() - source.columnIndex(); + + return goUpBlack(rowDiff, colDiff) || goUpUpBlack(rowDiff, colDiff, source) || goCrossBlack(rowDiff, colDiff, isExistOtherPiece); + } + + private boolean goCrossBlack(final int rowDiff, final int colDiff, final boolean isExistOtherPiece) { + return rowDiff == 1 && Math.abs(colDiff) == 1 && isExistOtherPiece; + } + + private boolean goUpBlack(int rowDiff, int colDiff) { + return rowDiff == 1 && colDiff == 0; + } + + private boolean goUpUpBlack(int rowDiff, int colDiff, Position source) { + return rowDiff == 2 && colDiff == 0 && isInitialPosition(source); + } + + public boolean isInitialPosition(final Position position) { + if (teamColor == TeamColor.BLACK) { + return position.rank() == Rank.SEVEN; + } + + return position.rank() == Rank.TWO; + } +} From 685eac49fc3d2ea5939141c8c571c5fa6fa31e72 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 03:26:41 +0900 Subject: [PATCH 19/54] =?UTF-8?q?test(boardTest):=20=EA=B8=B0=EB=AC=BC?= =?UTF-8?q?=EC=9D=84=20=EC=9D=B4=EB=8F=99=EC=8B=9C=ED=82=A4=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=98=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/test/java/domain/BoardTest.java | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java index 9e4a8ca1962..d7b4a0a35a3 100644 --- a/src/test/java/domain/BoardTest.java +++ b/src/test/java/domain/BoardTest.java @@ -1,8 +1,16 @@ package domain; +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; +import domain.strategy.PawnMoveStrategy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.HashMap; +import java.util.Map; + +import static domain.TeamColor.WHITE; import static org.assertj.core.api.Assertions.assertThat; class BoardTest { @@ -16,4 +24,60 @@ void createBoard() { // Then assertThat(board).isNotNull(); } + +// @DisplayName("기물을 이동시킨다.") +// @Test +// void movePieceTest() { +// // Given +// Piece piece = new Piece(PieceType.WHITE_BISHOP, new ContinuousMoveStrategy(diagonalVectors(), 8)); +// Position source = new Position(File.B, Rank.TWO); +// Position destination = new Position(File.D, Rank.FOUR); +// Map piecePositions = new HashMap<>(Map.of(source, piece)); +// Board board = new Board(piecePositions); +// +// // When +// board.movePiece(WHITE, source, destination); +// +// // Then +// assertThat(piecePositions.containsKey(destination)).isTrue(); +// assertThat(piecePositions.containsKey(source)).isFalse(); +// } + +// @DisplayName("기물을 이동시킨다.") +// @Test +// void movePieceTest() { +// // Given +// Piece piece = new Piece(PieceType.WHITE_KNIGHT, new KnightMoveStrategy()); +// Position source = new Position(File.B, Rank.TWO); +// Position destination = new Position(File.C, Rank.FOUR); +// Map piecePositions = new HashMap<>(Map.of(source, piece)); +// Board board = new Board(piecePositions); +// +// // When +// board.movePiece(WHITE, source, destination); +// +// // Then +// assertThat(piecePositions.containsKey(destination)).isTrue(); +// assertThat(piecePositions.containsKey(source)).isFalse(); +// } + + @DisplayName("기물을 이동시킨다.") + @Test + void movePieceTest() { + // Given + Piece piece = new Piece(PieceType.WHITE_PAWN, new PawnMoveStrategy(WHITE)); + Position source = new Position(File.C, Rank.TWO); + Position destination = new Position(File.D, Rank.THREE); + + + Map piecePositions = new HashMap<>(Map.of(source, piece, destination, new Piece(PieceType.BLACK_KING, null))); + Board board = new Board(piecePositions); + + // When + board.movePiece(WHITE, source, destination); + + // Then + assertThat(piecePositions.containsKey(destination)).isTrue(); + assertThat(piecePositions.containsKey(source)).isFalse(); + } } From 832de3e5f91e549284cd8c8399a1cbe12f3b59de Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 05:33:53 +0900 Subject: [PATCH 20/54] =?UTF-8?q?refactor(board):=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=B6=88=EA=B0=80=EB=8A=A5=ED=95=9C=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EB=B3=84=20=EC=98=88=EC=99=B8=EC=97=90=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/Board.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java index 4b01bdf0321..201a8fff0fd 100644 --- a/src/main/java/domain/Board.java +++ b/src/main/java/domain/Board.java @@ -23,13 +23,13 @@ public void movePiece(final TeamColor teamColor, final Position source, final Po Piece piece = chessBoard.get(source); if (!piece.isMovable(source, destination, parseOtherPiecePositions(source))) { - throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + throw new IllegalArgumentException("이동 위치까지 이동할 수 없습니다."); } // 이동 위치에 아군 기물이 존재 if (isPieceExist(destination)) { if (chessBoard.get(destination).hasColor(teamColor)) { - throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + throw new IllegalArgumentException("이동 위치에 아군 기물이 존재합니다."); } } // 공격 없이 빈 칸으로 이동하는 경우 @@ -45,7 +45,7 @@ private Set parseOtherPiecePositions(final Position source) { private void validateRequest(final TeamColor teamColor, final Position source) { if (!chessBoard.containsKey(source) || !chessBoard.get(source).hasColor(teamColor)) { - throw new IllegalArgumentException("유효하지 않은 기물 이동입니다."); + throw new IllegalArgumentException("차례가 맞지 않습니다."); } } From 161743dcc434aee4ac63c354ee027a2329756ffe Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 05:34:23 +0900 Subject: [PATCH 21/54] =?UTF-8?q?feat(inputView):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EC=9D=98=20=EB=AA=85=EB=A0=B9=EC=9D=84=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=B0=9B=EB=8A=94=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 Co-authored-by: Hyunguk Ryu --- src/main/java/domain/GameCommand.java | 6 +++- src/main/java/dto/RequestDto.java | 17 +++++----- src/main/java/view/InputView.java | 16 ++++++++-- src/main/java/view/PositionConvertor.java | 38 +++++++++++++++++++++++ 4 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 src/main/java/view/PositionConvertor.java diff --git a/src/main/java/domain/GameCommand.java b/src/main/java/domain/GameCommand.java index f2192670994..ed7f12bbcb6 100644 --- a/src/main/java/domain/GameCommand.java +++ b/src/main/java/domain/GameCommand.java @@ -1,5 +1,9 @@ package domain; public enum GameCommand { - START, MOVE, END + START, MOVE, END; + + public boolean isContinuable() { + return this != END; + } } diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java index 9c9c43be470..ff368c20c02 100644 --- a/src/main/java/dto/RequestDto.java +++ b/src/main/java/dto/RequestDto.java @@ -1,16 +1,17 @@ package dto; -import domain.position.File; import domain.GameCommand; -import domain.position.Rank; +import domain.position.Position; -public record RequestDto(GameCommand gameCommand, Rank rank, File file) { - public static RequestDto of(GameCommand gameCommand) { - // TODO: null 값 해결 - return new RequestDto(gameCommand, null, null); +import java.util.Optional; + +public record RequestDto(GameCommand command, Optional source, Optional destination) { + + public static RequestDto of(GameCommand command) { + return new RequestDto(command, Optional.empty(), Optional.empty()); } - public static RequestDto of(GameCommand gameCommand, Rank rank, File file) { - return new RequestDto(gameCommand, rank, file); + public static RequestDto of(GameCommand command, Position source, Position destination) { + return new RequestDto(command, Optional.of(source), Optional.of(destination)); } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index ca2cdc3a4cd..127da1ce8a3 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -3,25 +3,35 @@ import domain.GameCommand; import dto.RequestDto; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Scanner; +import static view.PositionConvertor.*; + public class InputView { private static final String START_COMMAND = "start"; + private static final String MOVE_COMMAND = "move"; private static final String END_COMMAND = "end"; private static final Map gameCommands = Map.of( START_COMMAND, GameCommand.START, + MOVE_COMMAND, GameCommand.MOVE, END_COMMAND, GameCommand.END ); private final Scanner sc = new Scanner(System.in); public RequestDto inputGameCommand() { - String input = sc.nextLine(); - if (!gameCommands.containsKey(input)) { + List input = Arrays.stream(sc.nextLine().split(" ")).toList(); + if (!gameCommands.containsKey(input.get(0))) { throw new IllegalArgumentException("유효하지 않은 명령입니다."); } - return RequestDto.of(gameCommands.get(input)); + if (input.size() == 3) { + return RequestDto.of(gameCommands.get(input.get(0)), convertPosition(input.get(1)), convertPosition(input.get(2))); + } + + return RequestDto.of(gameCommands.get(input.get(0))); } } diff --git a/src/main/java/view/PositionConvertor.java b/src/main/java/view/PositionConvertor.java new file mode 100644 index 00000000000..6b4950e245d --- /dev/null +++ b/src/main/java/view/PositionConvertor.java @@ -0,0 +1,38 @@ +package view; + +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; + +import java.util.Map; + +import static domain.position.File.*; +import static domain.position.Rank.*; + +public class PositionConvertor { + private static final Map files = Map.of( + "a", A, + "b", B, + "c", C, + "d", D, + "e", E, + "f", F, + "g", G, + "h", H + ); + private static final Map ranks = Map.of( + "1", ONE, + "2", TWO, + "3", THREE, + "4", FOUR, + "5", FIVE, + "6", SIX, + "7", SEVEN, + "8", EIGHT + ); + + public static Position convertPosition(String positionString) { + String[] split = positionString.split(""); + return new Position(files.get(split[0]), ranks.get(split[1])); + } +} From ba7a3804726434412122f0ea5ffd3102ea5793bb Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 05:35:19 +0900 Subject: [PATCH 22/54] =?UTF-8?q?feat(chessController):=20=ED=8C=80?= =?UTF-8?q?=EC=9D=84=20=EB=B2=88=EA=B0=88=EC=95=84=20=EA=B0=80=EB=A9=B0=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=9D=84=20=EC=A7=84=ED=96=89=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=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 Co-authored-by: Hyunguk Ryu --- src/main/java/controller/ChessController.java | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 18df5c407dd..1bc3b524e4f 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -2,7 +2,7 @@ import domain.Board; import domain.BoardInitializer; -import domain.GameCommand; +import domain.TeamColor; import dto.BoardDto; import dto.RequestDto; import view.InputView; @@ -18,21 +18,36 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - outputView.printWelcomeMessage(); RequestDto requestDto = inputView.inputGameCommand(); - - if (requestDto.gameCommand() == GameCommand.END) { - return; + Board board = BoardInitializer.init(); + printStatus(board); + + boolean isWhiteTurn = true; + + requestDto = inputView.inputGameCommand(); + while (requestDto.command().isContinuable()) { + try { + board.movePiece(defineTeam(isWhiteTurn), requestDto.source().get(), requestDto.destination().get()); + isWhiteTurn = !isWhiteTurn; + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + printStatus(board); + requestDto = inputView.inputGameCommand(); } - - startGame(); } - private void startGame() { - Board board = BoardInitializer.init(); + private TeamColor defineTeam(boolean isWhiteTurn) { + if (isWhiteTurn) { + return TeamColor.WHITE; + } + return TeamColor.BLACK; + } + private void printStatus(Board board) { BoardDto boardDto = BoardDto.from(board); - outputView.printBoard(boardDto); + System.out.println(); } + } From a87e56b58b9722d1062f0f49f091ec57c58b17e3 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 10:59:25 +0900 Subject: [PATCH 23/54] =?UTF-8?q?test(knightMoveStrategyTest):=20=EB=82=98?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=9B=80=EC=A7=81=EC=9E=84=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=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 Co-authored-by: Hyunguk Ryu --- .../strategy/KnightMoveStrategyTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/java/domain/strategy/KnightMoveStrategyTest.java diff --git a/src/test/java/domain/strategy/KnightMoveStrategyTest.java b/src/test/java/domain/strategy/KnightMoveStrategyTest.java new file mode 100644 index 00000000000..944e391d6db --- /dev/null +++ b/src/test/java/domain/strategy/KnightMoveStrategyTest.java @@ -0,0 +1,47 @@ +package domain.strategy; + +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class KnightMoveStrategyTest { + + @DisplayName("나이트가 이동할 수 있는 출발지/도착지면 true를 반환한다.") + @Test + void isMovableTest() { + // Given + Position source = new Position(File.C, Rank.TWO); + Position destination = new Position(File.D, Rank.FOUR); + Set otherPositions = Set.of(new Position(File.C, Rank.THREE)); + KnightMoveStrategy knightMoveStrategy = new KnightMoveStrategy(); + + // When + boolean movable = knightMoveStrategy.isMovable(source, destination, otherPositions); + + // Then + assertThat(movable).isTrue(); + } + + @DisplayName("나이트가 이동할 수 없는 출발지/도착지면 false를 반환한다.") + @Test + void isNotMovableTest() { + // Given + Position source = new Position(File.C, Rank.TWO); + Position destination = new Position(File.G, Rank.FIVE); + Set otherPositions = Set.of(new Position(File.C, Rank.THREE)); + KnightMoveStrategy knightMoveStrategy = new KnightMoveStrategy(); + + // When + boolean movable = knightMoveStrategy.isMovable(source, destination, otherPositions); + + // Then + assertThat(movable).isFalse(); + } + +} From 80b5cc2f6aa50ee55f698ac566286c48f2f1cb94 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 10:59:39 +0900 Subject: [PATCH 24/54] =?UTF-8?q?test(pawnMoveStrategyTest):=20=ED=8F=B0?= =?UTF-8?q?=20=EC=9B=80=EC=A7=81=EC=9E=84=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- .../domain/strategy/PawnMoveStrategyTest.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/java/domain/strategy/PawnMoveStrategyTest.java diff --git a/src/test/java/domain/strategy/PawnMoveStrategyTest.java b/src/test/java/domain/strategy/PawnMoveStrategyTest.java new file mode 100644 index 00000000000..ab2ab091341 --- /dev/null +++ b/src/test/java/domain/strategy/PawnMoveStrategyTest.java @@ -0,0 +1,73 @@ +package domain.strategy; + +import domain.TeamColor; +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; +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.Collections; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class PawnMoveStrategyTest { + @DisplayName("폰이 이동할 수 있는 출발/도착 위치면 true를 반환한다.") + @MethodSource("isMovableCase") + @ParameterizedTest + void isMovableTest(TeamColor teamColor, Position source, Position destination) { + // Given + PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(teamColor); + Set otherPositions = Set.of(new Position(File.D, Rank.FIVE), new Position(File.B, Rank.FOUR)); + + // When + boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); + + // Then + assertThat(movable).isTrue(); + } + + private static Stream isMovableCase() { + return Stream.of( + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.TWO), new Position(File.C, Rank.FOUR)), + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.FIVE)), + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.D, Rank.FIVE)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.SEVEN), new Position(File.C, Rank.FIVE)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.C, Rank.FOUR)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FOUR)) + ); + } + + @DisplayName("폰이 이동할 수 없는 출발/도착 위치면 false를 반환한다.") + @MethodSource("isNotMovableCase") + @ParameterizedTest + void isNotMovableTest(TeamColor teamColor, Position source, Position destination) { + // Given + PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(teamColor); + Set otherPositions = Collections.emptySet(); + + // When + boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); + + // Then + assertThat(movable).isFalse(); + } + + private static Stream isNotMovableCase() { + return Stream.of( + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.SIX)), + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.THREE)), + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.D, Rank.FOUR)), + Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.B, Rank.FIVE)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.SIX), new Position(File.C, Rank.FOUR)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.C, Rank.SIX)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FIVE)), + Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FOUR)) + ); + } + +} From b9c04c9f474be97d13b8e1f82a76a626f60cb70f Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 11:02:11 +0900 Subject: [PATCH 25/54] =?UTF-8?q?style(positionTest):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=84=A4=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/test/java/domain/PositionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/PositionTest.java index 075bb711555..0d181597bff 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/PositionTest.java @@ -25,7 +25,7 @@ void createPositionTest() { assertThat(position.file()).isEqualTo(file); } - @DisplayName("백터 값을 넘기면 새로운 위치의 Position을 반환한다.") + @DisplayName("백터 값을 전달하면 새로운 위치의 Position을 반환한다.") @Test void addPositionTest() { // Given From e4dbb49072eeb3a52f51ade35df79abc8063e0c0 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 11:20:02 +0900 Subject: [PATCH 26/54] =?UTF-8?q?feat(pieceFactory):=20PieceType=EC=9D=84?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC=EB=B0=9B=EC=95=84=20=EC=98=AC=EB=B0=94?= =?UTF-8?q?=EB=A5=B8=20Piece=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EB=8A=94=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 Co-authored-by: Hyunguk Ryu --- src/main/java/domain/BoardInitializer.java | 31 +--------------- src/main/java/domain/PieceFactory.java | 42 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 src/main/java/domain/PieceFactory.java diff --git a/src/main/java/domain/BoardInitializer.java b/src/main/java/domain/BoardInitializer.java index 15c363be81c..1a539a9d7d3 100644 --- a/src/main/java/domain/BoardInitializer.java +++ b/src/main/java/domain/BoardInitializer.java @@ -2,11 +2,6 @@ import domain.position.File; import domain.position.Position; -import domain.position.UnitVector; -import domain.strategy.ContinuousMoveStrategy; -import domain.strategy.KnightMoveStrategy; -import domain.strategy.MoveStrategy; -import domain.strategy.PawnMoveStrategy; import java.util.Arrays; import java.util.HashMap; @@ -18,16 +13,6 @@ import static domain.position.Rank.*; public class BoardInitializer { - private static final int MAXIMUM_MOVE_BOUND = 8; - private static final int KING_MAXIMUM_MOVE_BOUND = 1; - - private static final MoveStrategy whitePawnStrategy = new PawnMoveStrategy(TeamColor.WHITE); - private static final MoveStrategy blackPawnStrategy = new PawnMoveStrategy(TeamColor.BLACK); - private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); - private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(UnitVector.orthogonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(UnitVector.diagonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); private static final Map> pieceInitialPositions = Map.ofEntries( Map.entry(BLACK_PAWN, Arrays.stream(File.values()).map(file -> new Position(file, SEVEN)).toList()), Map.entry(BLACK_ROOK, List.of(new Position(A, EIGHT), new Position(H, EIGHT))), @@ -42,25 +27,11 @@ public class BoardInitializer { Map.entry(WHITE_QUEEN, List.of(new Position(D, ONE))), Map.entry(WHITE_KING, List.of(new Position(E, ONE))) ); - private static final Map moveStrategies = Map.ofEntries( - Map.entry(BLACK_PAWN, blackPawnStrategy), - Map.entry(BLACK_ROOK, rookStrategy), - Map.entry(BLACK_KNIGHT, knightStrategy), - Map.entry(BLACK_BISHOP, bishopStrategy), - Map.entry(BLACK_QUEEN, queenStrategy), - Map.entry(BLACK_KING, kingStrategy), - Map.entry(WHITE_PAWN, whitePawnStrategy), - Map.entry(WHITE_ROOK, rookStrategy), - Map.entry(WHITE_KNIGHT, knightStrategy), - Map.entry(WHITE_BISHOP, bishopStrategy), - Map.entry(WHITE_QUEEN, queenStrategy), - Map.entry(WHITE_KING, kingStrategy) - ); public static Board init() { Map positions = new HashMap<>(); pieceInitialPositions.forEach(((pieceType, position) -> { - position.forEach(p -> positions.put(p, new Piece(pieceType, moveStrategies.get(pieceType)))); + position.forEach(p -> positions.put(p, PieceFactory.create(pieceType))); })); return new Board(positions); diff --git a/src/main/java/domain/PieceFactory.java b/src/main/java/domain/PieceFactory.java new file mode 100644 index 00000000000..a375ff35225 --- /dev/null +++ b/src/main/java/domain/PieceFactory.java @@ -0,0 +1,42 @@ +package domain; + +import domain.position.UnitVector; +import domain.strategy.ContinuousMoveStrategy; +import domain.strategy.KnightMoveStrategy; +import domain.strategy.MoveStrategy; +import domain.strategy.PawnMoveStrategy; + +import java.util.Map; + +import static domain.PieceType.*; + +public class PieceFactory { + private static final int MAXIMUM_MOVE_BOUND = 8; + private static final int KING_MAXIMUM_MOVE_BOUND = 1; + private static final MoveStrategy whitePawnStrategy = new PawnMoveStrategy(TeamColor.WHITE); + private static final MoveStrategy blackPawnStrategy = new PawnMoveStrategy(TeamColor.BLACK); + private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); + private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(UnitVector.orthogonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(UnitVector.diagonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); + + private static final Map moveStrategies = Map.ofEntries( + Map.entry(BLACK_PAWN, blackPawnStrategy), + Map.entry(BLACK_ROOK, rookStrategy), + Map.entry(BLACK_KNIGHT, knightStrategy), + Map.entry(BLACK_BISHOP, bishopStrategy), + Map.entry(BLACK_QUEEN, queenStrategy), + Map.entry(BLACK_KING, kingStrategy), + Map.entry(WHITE_PAWN, whitePawnStrategy), + Map.entry(WHITE_ROOK, rookStrategy), + Map.entry(WHITE_KNIGHT, knightStrategy), + Map.entry(WHITE_BISHOP, bishopStrategy), + Map.entry(WHITE_QUEEN, queenStrategy), + Map.entry(WHITE_KING, kingStrategy) + ); + + public static Piece create(PieceType pieceType) { + return new Piece(pieceType, moveStrategies.get(pieceType)); + } +} From e4f2c1104d150f6d520a47f046ec76cdc7865010 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 11:36:03 +0900 Subject: [PATCH 27/54] =?UTF-8?q?test(boardTest):=20Board=20=EC=B6=9C?= =?UTF-8?q?=EB=B0=9C=EC=A7=80=20->=20=EB=AA=A9=EC=A0=81=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EB=AC=BC=20=EB=B0=B0=EC=B9=98=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?&=20=EC=8B=A4=ED=8C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/test/java/domain/BoardTest.java | 90 +++++++++++++++-------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java index d7b4a0a35a3..79226935db6 100644 --- a/src/test/java/domain/BoardTest.java +++ b/src/test/java/domain/BoardTest.java @@ -3,7 +3,6 @@ import domain.position.File; import domain.position.Position; import domain.position.Rank; -import domain.strategy.PawnMoveStrategy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,6 +11,7 @@ import static domain.TeamColor.WHITE; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class BoardTest { @@ -25,52 +25,36 @@ void createBoard() { assertThat(board).isNotNull(); } -// @DisplayName("기물을 이동시킨다.") -// @Test -// void movePieceTest() { -// // Given -// Piece piece = new Piece(PieceType.WHITE_BISHOP, new ContinuousMoveStrategy(diagonalVectors(), 8)); -// Position source = new Position(File.B, Rank.TWO); -// Position destination = new Position(File.D, Rank.FOUR); -// Map piecePositions = new HashMap<>(Map.of(source, piece)); -// Board board = new Board(piecePositions); -// -// // When -// board.movePiece(WHITE, source, destination); -// -// // Then -// assertThat(piecePositions.containsKey(destination)).isTrue(); -// assertThat(piecePositions.containsKey(source)).isFalse(); -// } - -// @DisplayName("기물을 이동시킨다.") -// @Test -// void movePieceTest() { -// // Given -// Piece piece = new Piece(PieceType.WHITE_KNIGHT, new KnightMoveStrategy()); -// Position source = new Position(File.B, Rank.TWO); -// Position destination = new Position(File.C, Rank.FOUR); -// Map piecePositions = new HashMap<>(Map.of(source, piece)); -// Board board = new Board(piecePositions); -// -// // When -// board.movePiece(WHITE, source, destination); -// -// // Then -// assertThat(piecePositions.containsKey(destination)).isTrue(); -// assertThat(piecePositions.containsKey(source)).isFalse(); -// } - - @DisplayName("기물을 이동시킨다.") + @DisplayName("기물의 이동 목적지가 비어있으면 이동시킬 수 있다.") @Test - void movePieceTest() { + void movePieceToEmptySpaceTest() { // Given - Piece piece = new Piece(PieceType.WHITE_PAWN, new PawnMoveStrategy(WHITE)); - Position source = new Position(File.C, Rank.TWO); - Position destination = new Position(File.D, Rank.THREE); + Piece piece = PieceFactory.create(PieceType.WHITE_BISHOP); + Position source = new Position(File.B, Rank.TWO); + Position destination = new Position(File.D, Rank.FOUR); + Map piecePositions = new HashMap<>(Map.of(source, piece)); + Board board = new Board(piecePositions); + // When + board.movePiece(WHITE, source, destination); + + // Then + assertThat(piecePositions.containsKey(destination)).isTrue(); + assertThat(piecePositions.containsKey(source)).isFalse(); + } - Map piecePositions = new HashMap<>(Map.of(source, piece, destination, new Piece(PieceType.BLACK_KING, null))); + @DisplayName("기물의 이동 목적지에 다른 색의 기물이 있으면 이동시킬 수 있다.") + @Test + void movePieceToEnemySpaceTest() { + // Given + Piece piece = PieceFactory.create(PieceType.WHITE_KNIGHT); + Piece enemy = PieceFactory.create(PieceType.BLACK_KING); + Position source = new Position(File.B, Rank.TWO); + Position destination = new Position(File.C, Rank.FOUR); + Map piecePositions = new HashMap<>(Map.of( + source, piece, + destination, enemy + )); Board board = new Board(piecePositions); // When @@ -80,4 +64,24 @@ void movePieceTest() { assertThat(piecePositions.containsKey(destination)).isTrue(); assertThat(piecePositions.containsKey(source)).isFalse(); } + + @DisplayName("기물의 이동 목적지에 같은 색의 기물이 있으면 이동시킬 수 없다.") + @Test + void notMovePieceTest() { + // Given + Piece piece = PieceFactory.create(PieceType.WHITE_KNIGHT); + Piece other = PieceFactory.create(PieceType.WHITE_ROOK); + Position source = new Position(File.B, Rank.TWO); + Position destination = new Position(File.C, Rank.FOUR); + Map piecePositions = new HashMap<>(Map.of( + source, piece, + destination, other + )); + Board board = new Board(piecePositions); + + // When & Then + assertThatThrownBy(() -> board.movePiece(WHITE, source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동 위치에 아군 기물이 존재합니다."); + } } From 5df2629883f9fffbc9e321119ed6a50e116cf8c9 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 11:41:48 +0900 Subject: [PATCH 28/54] =?UTF-8?q?refactor(continuousMoveStrategy):=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/strategy/ContinuousMoveStrategy.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java index 07703a40cb4..6d0e8d4ae31 100644 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -25,15 +25,14 @@ public boolean isMovable(final Position source, final Position destination, fina } List movePaths = Stream.iterate(source, position -> position.add(optimalVector)) - .takeWhile(position -> isAlive(position, destination, piecePositions)) + .takeWhile(position -> isContinuable(position, destination, piecePositions)) .limit(moveBound) .toList(); return isReachable(destination, optimalVector, movePaths); } - // TODO: 메서드 이름 짓기 - private static boolean isAlive(final Position current, final Position destination, final Set piecePositions) { + private static boolean isContinuable(final Position current, final Position destination, final Set piecePositions) { boolean isReachedDestination = current.equals(destination); boolean isOtherPieceExist = piecePositions.contains(current); return !isReachedDestination && !isOtherPieceExist; From 78937a58c000aa01e477dff2c3582e99a726866a Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 13:48:07 +0900 Subject: [PATCH 29/54] =?UTF-8?q?style(position):=20=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20TODO=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/position/Position.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java index f3b9c9affe6..ee1608fea1f 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/position/Position.java @@ -1,6 +1,5 @@ package domain.position; -// TODO: 캐싱 기법 고민하기 public record Position(File file, Rank rank) { public int rowIndex() { return rank.getIndex(); From 2c67d48b803f2d5927781d383dec7250cb301547 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 14:33:17 +0900 Subject: [PATCH 30/54] =?UTF-8?q?refactor(chessController):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9E=85=EB=A0=A5=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EA=B2=8C=EC=9E=84=EC=9D=84=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=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 Co-authored-by: Hyunguk Ryu --- src/main/java/controller/ChessController.java | 38 +++++++++++-------- src/main/java/domain/GameCommand.java | 4 ++ src/main/java/domain/TeamColor.java | 9 ++++- src/main/java/domain/Turn.java | 17 +++++++++ src/main/java/view/InputView.java | 10 ++++- src/main/java/view/OutputView.java | 8 ++-- 6 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 src/main/java/domain/Turn.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 1bc3b524e4f..7c2a804ffee 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -1,8 +1,8 @@ package controller; -import domain.Board; -import domain.BoardInitializer; -import domain.TeamColor; +//import domain.*; + +import domain.*; import dto.BoardDto; import dto.RequestDto; import view.InputView; @@ -18,25 +18,35 @@ public ChessController(final InputView inputView, final OutputView outputView) { } public void run() { - RequestDto requestDto = inputView.inputGameCommand(); + outputView.printWelcomeMessage(); + GameCommand command = inputView.inputGameStart(); + if (command.isStart()) { + startGame(); + } + } + + private void startGame() { Board board = BoardInitializer.init(); printStatus(board); - boolean isWhiteTurn = true; - - requestDto = inputView.inputGameCommand(); + Turn turn = new Turn(); + RequestDto requestDto = inputView.inputGameCommand(); while (requestDto.command().isContinuable()) { - try { - board.movePiece(defineTeam(isWhiteTurn), requestDto.source().get(), requestDto.destination().get()); - isWhiteTurn = !isWhiteTurn; - } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); - } + doTurn(board, turn, requestDto); printStatus(board); requestDto = inputView.inputGameCommand(); } } + private void doTurn(final Board board, Turn turn, final RequestDto requestDto) { + try { + board.movePiece(turn.current(), requestDto.source().get(), requestDto.destination().get()); + turn.next(); + } catch (IllegalArgumentException e) { + System.out.println("[오류] " + e.getMessage()); + } + } + private TeamColor defineTeam(boolean isWhiteTurn) { if (isWhiteTurn) { return TeamColor.WHITE; @@ -47,7 +57,5 @@ private TeamColor defineTeam(boolean isWhiteTurn) { private void printStatus(Board board) { BoardDto boardDto = BoardDto.from(board); outputView.printBoard(boardDto); - System.out.println(); } - } diff --git a/src/main/java/domain/GameCommand.java b/src/main/java/domain/GameCommand.java index ed7f12bbcb6..3d38ad62863 100644 --- a/src/main/java/domain/GameCommand.java +++ b/src/main/java/domain/GameCommand.java @@ -6,4 +6,8 @@ public enum GameCommand { public boolean isContinuable() { return this != END; } + + public boolean isStart() { + return this == START; + } } diff --git a/src/main/java/domain/TeamColor.java b/src/main/java/domain/TeamColor.java index 16416b5fadc..1d92bfaf726 100644 --- a/src/main/java/domain/TeamColor.java +++ b/src/main/java/domain/TeamColor.java @@ -1,5 +1,12 @@ package domain; public enum TeamColor { - BLACK, WHITE + BLACK, WHITE; + + public TeamColor toggle() { + if (this == BLACK) { + return WHITE; + } + return BLACK; + } } diff --git a/src/main/java/domain/Turn.java b/src/main/java/domain/Turn.java new file mode 100644 index 00000000000..d5a082e331f --- /dev/null +++ b/src/main/java/domain/Turn.java @@ -0,0 +1,17 @@ +package domain; + +public class Turn { + TeamColor currentTurn; + + public Turn() { + this.currentTurn = TeamColor.WHITE; + } + + public void next() { + currentTurn = currentTurn.toggle(); + } + + public TeamColor current() { + return currentTurn; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 127da1ce8a3..ea9ecbb41e1 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -8,7 +8,7 @@ import java.util.Map; import java.util.Scanner; -import static view.PositionConvertor.*; +import static view.PositionConvertor.convertPosition; public class InputView { private static final String START_COMMAND = "start"; @@ -22,6 +22,14 @@ public class InputView { private final Scanner sc = new Scanner(System.in); + public GameCommand inputGameStart() { + String input = sc.nextLine(); + if (!gameCommands.containsKey(input)) { + throw new IllegalArgumentException("잘못된 명령입니다."); + } + return gameCommands.get(input); + } + public RequestDto inputGameCommand() { List input = Arrays.stream(sc.nextLine().split(" ")).toList(); if (!gameCommands.containsKey(input.get(0))) { diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index a458fb78cff..82f25fe0aef 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -14,14 +14,16 @@ public class OutputView { private final OutputConvertor outputConvertor = new OutputConvertor(); public void printWelcomeMessage() { - System.out.println("체스 게임을 시작합니다."); - System.out.println("게임 시작은 start, 종료는 end 명령을 입력하세요."); + System.out.println("> 체스 게임을 시작합니다."); + System.out.println("> 게임 시작: start"); + System.out.println("> 게임 종료: end"); + System.out.println("> 게임 이동: move source위치 target위치 - 예. move b2 b3"); } public void printBoard(final BoardDto boardDto) { List boardStatus = convertBoardStatus(boardDto.piecePositions()); - boardStatus.forEach(System.out::println); + System.out.println(); } private List convertBoardStatus(final Map boardStatus) { From 42f87da99e5e570d26d07b5917db3dcb0de006ce Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 14:41:22 +0900 Subject: [PATCH 31/54] =?UTF-8?q?refactor(requestDto):=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=20=EC=9D=B8=EC=9E=90=EC=97=90=EC=84=9C=20Optional=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/controller/ChessController.java | 2 +- src/main/java/dto/MovePositionDto.java | 29 +++++++++++++++++++ src/main/java/dto/RequestDto.java | 16 ++++++---- 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 src/main/java/dto/MovePositionDto.java diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java index 7c2a804ffee..1334a477834 100644 --- a/src/main/java/controller/ChessController.java +++ b/src/main/java/controller/ChessController.java @@ -40,7 +40,7 @@ private void startGame() { private void doTurn(final Board board, Turn turn, final RequestDto requestDto) { try { - board.movePiece(turn.current(), requestDto.source().get(), requestDto.destination().get()); + board.movePiece(turn.current(), requestDto.source(), requestDto.destination()); turn.next(); } catch (IllegalArgumentException e) { System.out.println("[오류] " + e.getMessage()); diff --git a/src/main/java/dto/MovePositionDto.java b/src/main/java/dto/MovePositionDto.java new file mode 100644 index 00000000000..a914b9ef7d7 --- /dev/null +++ b/src/main/java/dto/MovePositionDto.java @@ -0,0 +1,29 @@ +package dto; + +import domain.position.Position; + +import java.util.Collections; +import java.util.List; + +public record MovePositionDto(List positions) { + + public static MovePositionDto noPosition() { + return new MovePositionDto(Collections.emptyList()); + } + + public static MovePositionDto of(final Position source, final Position destination) { + return new MovePositionDto(List.of(source, destination)); + } + + public boolean hasPosition() { + return !positions.isEmpty(); + } + + public Position source() { + return positions.get(0); + } + + public Position destination() { + return positions.get(1); + } +} diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java index ff368c20c02..7de58fc67c9 100644 --- a/src/main/java/dto/RequestDto.java +++ b/src/main/java/dto/RequestDto.java @@ -3,15 +3,21 @@ import domain.GameCommand; import domain.position.Position; -import java.util.Optional; - -public record RequestDto(GameCommand command, Optional source, Optional destination) { +public record RequestDto(GameCommand command, MovePositionDto movePositionDto) { public static RequestDto of(GameCommand command) { - return new RequestDto(command, Optional.empty(), Optional.empty()); + return new RequestDto(command, MovePositionDto.noPosition()); } public static RequestDto of(GameCommand command, Position source, Position destination) { - return new RequestDto(command, Optional.of(source), Optional.of(destination)); + return new RequestDto(command, MovePositionDto.of(source, destination)); + } + + public Position source() { + return movePositionDto.source(); + } + + public Position destination() { + return movePositionDto.destination(); } } From a99a608d648ca202746c9585522aace751267670 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Thu, 21 Mar 2024 14:59:26 +0900 Subject: [PATCH 32/54] =?UTF-8?q?fix(pawnMoveStrategy):=20=ED=8F=B0?= =?UTF-8?q?=EC=9D=B4=20=EC=A7=81=EC=84=A0=20=EC=9D=B4=EB=8F=99=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=83=81=EB=8C=80=20=EA=B8=B0=EB=AC=BC=EC=9D=84=20?= =?UTF-8?q?=EA=B3=B5=EA=B2=A9=ED=95=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- .../domain/strategy/PawnMoveStrategy.java | 22 ++++++++++++------- .../domain/strategy/PawnMoveStrategyTest.java | 18 +++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/java/domain/strategy/PawnMoveStrategy.java b/src/main/java/domain/strategy/PawnMoveStrategy.java index 392f427a109..6f955935ae3 100644 --- a/src/main/java/domain/strategy/PawnMoveStrategy.java +++ b/src/main/java/domain/strategy/PawnMoveStrategy.java @@ -25,11 +25,10 @@ private boolean isWhiteMovable(final Position source, final Position destination int rowDiff = destination.rowIndex() - source.rowIndex(); int colDiff = destination.columnIndex() - source.columnIndex(); - return goUpWhite(rowDiff, colDiff) || goUpUpWhite(rowDiff, colDiff, source) || goCrossWhite(rowDiff, colDiff, isExistOtherPiece); - } + boolean isStraightMovable = (goUpWhite(rowDiff, colDiff) || goUpUpWhite(rowDiff, colDiff, source)) && !isExistOtherPiece; + boolean isCrossMovable = goCrossWhite(rowDiff, colDiff) && isExistOtherPiece; - private boolean goCrossWhite(final int rowDiff, final int colDiff, final boolean isExistOtherPiece) { - return rowDiff == -1 && Math.abs(colDiff) == 1 && isExistOtherPiece; + return isStraightMovable || isCrossMovable; } private boolean goUpWhite(int rowDiff, int colDiff) { @@ -40,16 +39,19 @@ private boolean goUpUpWhite(int rowDiff, int colDiff, Position source) { return rowDiff == -2 && colDiff == 0 && isInitialPosition(source); } + private boolean goCrossWhite(final int rowDiff, final int colDiff) { + return rowDiff == -1 && Math.abs(colDiff) == 1; + } + private boolean isBlackMovable(final Position source, final Position destination, final boolean isExistOtherPiece) { int rowDiff = destination.rowIndex() - source.rowIndex(); int colDiff = destination.columnIndex() - source.columnIndex(); - return goUpBlack(rowDiff, colDiff) || goUpUpBlack(rowDiff, colDiff, source) || goCrossBlack(rowDiff, colDiff, isExistOtherPiece); - } + boolean isStraightMovable = (goUpBlack(rowDiff, colDiff) || goUpUpBlack(rowDiff, colDiff, source)) && !isExistOtherPiece; + boolean isCrossMovable = goCrossBlack(rowDiff, colDiff) && isExistOtherPiece; - private boolean goCrossBlack(final int rowDiff, final int colDiff, final boolean isExistOtherPiece) { - return rowDiff == 1 && Math.abs(colDiff) == 1 && isExistOtherPiece; + return isStraightMovable || isCrossMovable; } private boolean goUpBlack(int rowDiff, int colDiff) { @@ -60,6 +62,10 @@ private boolean goUpUpBlack(int rowDiff, int colDiff, Position source) { return rowDiff == 2 && colDiff == 0 && isInitialPosition(source); } + private boolean goCrossBlack(final int rowDiff, final int colDiff) { + return rowDiff == 1 && Math.abs(colDiff) == 1; + } + public boolean isInitialPosition(final Position position) { if (teamColor == TeamColor.BLACK) { return position.rank() == Rank.SEVEN; diff --git a/src/test/java/domain/strategy/PawnMoveStrategyTest.java b/src/test/java/domain/strategy/PawnMoveStrategyTest.java index ab2ab091341..46007cdd156 100644 --- a/src/test/java/domain/strategy/PawnMoveStrategyTest.java +++ b/src/test/java/domain/strategy/PawnMoveStrategyTest.java @@ -5,11 +5,13 @@ import domain.position.Position; import domain.position.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.Collections; +import java.util.HashSet; import java.util.Set; import java.util.stream.Stream; @@ -70,4 +72,20 @@ private static Stream isNotMovableCase() { ); } + @DisplayName("폰이 전진한 위치에 기물이 존재하면 이동할 수 없다.") + @Test + void isNotMovableToStraight() { + // Given + PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(TeamColor.WHITE); + Position source = new Position(File.B, Rank.TWO); + Position destination = new Position(File.B, Rank.THREE); + Set otherPositions = new HashSet<>(Set.of(destination)); + + // When + boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); + + // Then + assertThat(movable).isFalse(); + } + } From 71d0c77f955d7852391cd85a53d11e1d65e5c093 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Fri, 22 Mar 2024 17:10:27 +0900 Subject: [PATCH 33/54] =?UTF-8?q?refactor:=20position=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85?= =?UTF-8?q?=EC=9D=84=20=EB=8D=94=20=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=A8.=20-=20=EB=B6=80=EC=A1=B1=ED=95=9C?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=A8.=20-=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=8D=94=20=EB=AA=85=ED=99=95=ED=95=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EA=B0=9C=EC=84=A0=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/PieceFactory.java | 10 +-- .../position/CommonMovementDirection.java | 66 ++++++++++++++++ src/main/java/domain/position/File.java | 8 +- .../java/domain/position/KnightDirection.java | 36 --------- .../position/KnightMovementDirection.java | 34 +++++++++ src/main/java/domain/position/Position.java | 15 ++-- src/main/java/domain/position/Rank.java | 8 +- .../strategy/ContinuousMoveStrategy.java | 18 ++--- .../domain/strategy/KnightMoveStrategy.java | 7 +- .../position/CommonMovementDirectionTest.java | 63 ++++++++++++++++ src/test/java/domain/position/FileTest.java | 35 +++++++++ .../position/KnightMovementDirectionTest.java | 75 +++++++++++++++++++ .../domain/{ => position}/PositionTest.java | 10 +-- src/test/java/domain/position/RankTest.java | 35 +++++++++ .../strategy/ContinuousMoveStrategyTest.java | 14 ++-- 15 files changed, 349 insertions(+), 85 deletions(-) create mode 100644 src/main/java/domain/position/CommonMovementDirection.java delete mode 100644 src/main/java/domain/position/KnightDirection.java create mode 100644 src/main/java/domain/position/KnightMovementDirection.java create mode 100644 src/test/java/domain/position/CommonMovementDirectionTest.java create mode 100644 src/test/java/domain/position/FileTest.java create mode 100644 src/test/java/domain/position/KnightMovementDirectionTest.java rename src/test/java/domain/{ => position}/PositionTest.java (73%) create mode 100644 src/test/java/domain/position/RankTest.java diff --git a/src/main/java/domain/PieceFactory.java b/src/main/java/domain/PieceFactory.java index a375ff35225..978b395e525 100644 --- a/src/main/java/domain/PieceFactory.java +++ b/src/main/java/domain/PieceFactory.java @@ -1,6 +1,6 @@ package domain; -import domain.position.UnitVector; +import domain.position.CommonMovementDirection; import domain.strategy.ContinuousMoveStrategy; import domain.strategy.KnightMoveStrategy; import domain.strategy.MoveStrategy; @@ -16,10 +16,10 @@ public class PieceFactory { private static final MoveStrategy whitePawnStrategy = new PawnMoveStrategy(TeamColor.WHITE); private static final MoveStrategy blackPawnStrategy = new PawnMoveStrategy(TeamColor.BLACK); private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); - private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(UnitVector.orthogonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(UnitVector.diagonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(UnitVector.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); + private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(CommonMovementDirection.orthogonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(CommonMovementDirection.diagonalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(CommonMovementDirection.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); + private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(CommonMovementDirection.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); private static final Map moveStrategies = Map.ofEntries( Map.entry(BLACK_PAWN, blackPawnStrategy), diff --git a/src/main/java/domain/position/CommonMovementDirection.java b/src/main/java/domain/position/CommonMovementDirection.java new file mode 100644 index 00000000000..df9b2565f0e --- /dev/null +++ b/src/main/java/domain/position/CommonMovementDirection.java @@ -0,0 +1,66 @@ +package domain.position; + +import java.util.Arrays; +import java.util.Set; +import java.util.function.BiPredicate; + +public enum CommonMovementDirection { + UP(-1, 0, (rowDistance, columnDistance) -> rowDistance < 0 && columnDistance == 0), + DOWN(1, 0, (rowDistance, columnDistance) -> rowDistance > 0 && columnDistance == 0), + RIGHT(0, 1, (rowDistance, columnDistance) -> rowDistance == 0 && columnDistance > 0), + LEFT(0, -1, (rowDistance, columnDistance) -> rowDistance == 0 && columnDistance < 0), + UP_RIGHT(-1, 1, (rowDistance, columnDistance) -> rowDistance < 0 && columnDistance > 0), + UP_LEFT(-1, -1, (rowDistance, columnDistance) -> rowDistance < 0 && columnDistance < 0), + DOWN_RIGHT(1, 1, (rowDistance, columnDistance) -> rowDistance > 0 && columnDistance > 0), + DOWN_LEFT(1, -1, (rowDistance, columnDistance) -> rowDistance > 0 && columnDistance < 0); + + private final int rowDistance; + private final int columnDistance; + private final BiPredicate condition; + + CommonMovementDirection(final int rowDistance, final int columnDistance, final BiPredicate condition) { + this.rowDistance = rowDistance; + this.columnDistance = columnDistance; + this.condition = condition; + } + + public static CommonMovementDirection find(final Position source, final Position destination) { + final int rowDifference = destination.rowIndex() - source.rowIndex(); + final int columnDifference = destination.columnIndex() - source.columnIndex(); + + validateDistance(rowDifference, columnDifference); + + return Arrays.stream(CommonMovementDirection.values()) + .filter(unitVector -> unitVector.condition.test(rowDifference, columnDifference)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("방향 계산이 불가능한 거리값입니다.")); + } + + private static void validateDistance(final int rowDistance, final int columnDistance) { + if (rowDistance == 0 && columnDistance == 0 + || (!(Math.abs(rowDistance) == Math.abs(columnDistance)) + && !(rowDistance == 0 || columnDistance == 0))) { + throw new IllegalArgumentException(("방향 계산이 불가능한 거리값입니다.")); + } + } + + public static Set orthogonalVectors() { + return Set.of(UP, RIGHT, DOWN, LEFT); + } + + public static Set diagonalVectors() { + return Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + } + + public static Set omnidirectionalVectors() { + return Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + } + + public int getRowDistance() { + return rowDistance; + } + + public int getColumnDistance() { + return columnDistance; + } +} diff --git a/src/main/java/domain/position/File.java b/src/main/java/domain/position/File.java index 5c6dc553f32..dd371f131cb 100644 --- a/src/main/java/domain/position/File.java +++ b/src/main/java/domain/position/File.java @@ -18,14 +18,14 @@ public enum File { this.index = index; } - public int getIndex() { - return index; - } - public static File of(final int index) { return Arrays.stream(values()) .filter(file -> file.getIndex() == index) .findAny() .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 인덱스입니다.")); } + + public int getIndex() { + return this.index; + } } diff --git a/src/main/java/domain/position/KnightDirection.java b/src/main/java/domain/position/KnightDirection.java deleted file mode 100644 index 3726f5a8fe1..00000000000 --- a/src/main/java/domain/position/KnightDirection.java +++ /dev/null @@ -1,36 +0,0 @@ -package domain.position; - -import java.util.Arrays; -import java.util.List; - -public enum KnightDirection { - UP_RIGHT(-2, 1), - RIGHT_UP(-1, 2), - RIGHT_DOWN(1, 2), - DOWN_RIGHT(2, 1), - DOWN_LEFT(2, -1), - LEFT_DOWN(1, -2), - LEFT_UP(-1, -2), - UP_LEFT(-2, -1); - - private final int row; - private final int col; - - KnightDirection(final int row, final int col) { - this.row = row; - this.col = col; - } - - private boolean hasPosition(int row, int col) { - return this.row == row && this.col == col; - } - - public static List asList() { - return Arrays.stream(values()).toList(); - } - - public static boolean isExist(int row, int col) { - return Arrays.stream(values()) - .anyMatch(knightDirection -> knightDirection.hasPosition(row, col)); - } -} diff --git a/src/main/java/domain/position/KnightMovementDirection.java b/src/main/java/domain/position/KnightMovementDirection.java new file mode 100644 index 00000000000..b117f99a023 --- /dev/null +++ b/src/main/java/domain/position/KnightMovementDirection.java @@ -0,0 +1,34 @@ +package domain.position; + +import java.util.Arrays; + +public enum KnightMovementDirection { + UP_RIGHT(-2, 1), + UP_LEFT(-2, -1), + DOWN_RIGHT(2, 1), + DOWN_LEFT(2, -1), + RIGHT_UP(-1, 2), + RIGHT_DOWN(1, 2), + LEFT_UP(-1, -2), + LEFT_DOWN(1, -2); + + private final int rowDistance; + private final int columnDistance; + + KnightMovementDirection(final int rowDistance, final int columnDistance) { + this.rowDistance = rowDistance; + this.columnDistance = columnDistance; + } + + public static boolean isMovableDirection(final Position source, final Position destination) { + return Arrays.stream(values()) + .anyMatch(knightDirection -> knightDirection.find(source, destination)); + } + + private boolean find(final Position source, final Position destination) { + final int rowDistance = destination.rowIndex() - source.rowIndex(); + final int columnDistance = destination.columnIndex() - source.columnIndex(); + + return this.rowDistance == rowDistance && this.columnDistance == columnDistance; + } +} diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java index ee1608fea1f..ba5742bf1ba 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/position/Position.java @@ -1,6 +1,14 @@ package domain.position; public record Position(File file, Rank rank) { + + public Position next(final CommonMovementDirection commonMovementDirection) { + File nextFile = File.of(this.columnIndex() + commonMovementDirection.getColumnDistance()); + Rank nextRank = Rank.of(this.rowIndex() + commonMovementDirection.getRowDistance()); + + return new Position(nextFile, nextRank); + } + public int rowIndex() { return rank.getIndex(); } @@ -8,11 +16,4 @@ public int rowIndex() { public int columnIndex() { return file.getIndex(); } - - public Position add(final UnitVector unitVector) { - File newFile = File.of(columnIndex() + unitVector.getCol()); - Rank newRank = Rank.of(rowIndex() + unitVector.getRow()); - - return new Position(newFile, newRank); - } } diff --git a/src/main/java/domain/position/Rank.java b/src/main/java/domain/position/Rank.java index 8e1258b126e..063ca9fd959 100644 --- a/src/main/java/domain/position/Rank.java +++ b/src/main/java/domain/position/Rank.java @@ -18,14 +18,14 @@ public enum Rank { this.index = index; } - public int getIndex() { - return index; - } - public static Rank of(final int index) { return Arrays.stream(Rank.values()) .filter(rank -> rank.getIndex() == index) .findAny() .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 인덱스입니다.")); } + + public int getIndex() { + return index; + } } diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java index 6d0e8d4ae31..acee4dc4027 100644 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -1,30 +1,30 @@ package domain.strategy; import domain.position.Position; -import domain.position.UnitVector; +import domain.position.CommonMovementDirection; import java.util.List; import java.util.Set; import java.util.stream.Stream; public class ContinuousMoveStrategy implements MoveStrategy { - private final Set acceptableVectors; + private final Set acceptableVectors; private final int moveBound; - public ContinuousMoveStrategy(Set acceptableVectors, int moveBound) { + public ContinuousMoveStrategy(Set acceptableVectors, int moveBound) { this.acceptableVectors = acceptableVectors; this.moveBound = moveBound; } @Override public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - UnitVector optimalVector = findOptimalVector(source, destination); + CommonMovementDirection optimalVector = findOptimalVector(source, destination); if (!acceptableVectors.contains(optimalVector)) { return false; } - List movePaths = Stream.iterate(source, position -> position.add(optimalVector)) + List movePaths = Stream.iterate(source, position -> position.next(optimalVector)) .takeWhile(position -> isContinuable(position, destination, piecePositions)) .limit(moveBound) .toList(); @@ -38,15 +38,15 @@ private static boolean isContinuable(final Position current, final Position dest return !isReachedDestination && !isOtherPieceExist; } - private boolean isReachable(final Position destination, final UnitVector optimalVector, final List movePaths) { - Position finalPosition = movePaths.get(movePaths.size() - 1).add(optimalVector); + private boolean isReachable(final Position destination, final CommonMovementDirection optimalVector, final List movePaths) { + Position finalPosition = movePaths.get(movePaths.size() - 1).next(optimalVector); return finalPosition.equals(destination); } - private UnitVector findOptimalVector(final Position source, final Position destination) { + private CommonMovementDirection findOptimalVector(final Position source, final Position destination) { int rowDiff = destination.rowIndex() - source.rowIndex(); int colDiff = destination.columnIndex() - source.columnIndex(); - return UnitVector.of(rowDiff, colDiff); + return CommonMovementDirection.find(source, destination); } } diff --git a/src/main/java/domain/strategy/KnightMoveStrategy.java b/src/main/java/domain/strategy/KnightMoveStrategy.java index 58a853256a8..26f546ce981 100644 --- a/src/main/java/domain/strategy/KnightMoveStrategy.java +++ b/src/main/java/domain/strategy/KnightMoveStrategy.java @@ -1,6 +1,6 @@ package domain.strategy; -import domain.position.KnightDirection; +import domain.position.KnightMovementDirection; import domain.position.Position; import java.util.Set; @@ -8,9 +8,6 @@ public class KnightMoveStrategy implements MoveStrategy { @Override public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - int rowDiff = destination.rowIndex() - source.rowIndex(); - int colDiff = destination.columnIndex() - source.columnIndex(); - - return KnightDirection.isExist(rowDiff, colDiff); + return KnightMovementDirection.isMovableDirection(source, destination); } } diff --git a/src/test/java/domain/position/CommonMovementDirectionTest.java b/src/test/java/domain/position/CommonMovementDirectionTest.java new file mode 100644 index 00000000000..dbe21b0711a --- /dev/null +++ b/src/test/java/domain/position/CommonMovementDirectionTest.java @@ -0,0 +1,63 @@ +package domain.position; + +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 domain.position.File.*; +import static domain.position.CommonMovementDirection.*; +import static domain.position.Rank.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CommonMovementDirectionTest { + + @DisplayName("입력받은 출발지/목적지의 거리를 바탕으로 이동방향을 반환한다.") + @MethodSource("findMovementDirectionTestCase") + @ParameterizedTest + void findMovementDirection(final Position source, final Position destination, final CommonMovementDirection expect) { + // When + CommonMovementDirection commonMovementDirection = CommonMovementDirection.find(source, destination); + + // Then + assertThat(commonMovementDirection).isEqualTo(expect); + } + + private static Stream findMovementDirectionTestCase() { + return Stream.of( + Arguments.of(position(B, TWO), position(B, SIX), UP), + Arguments.of(position(B, FIVE), position(B, TWO), DOWN), + Arguments.of(position(B, TWO), position(G, TWO), RIGHT), + Arguments.of(position(E, FIVE), position(B, FIVE), LEFT), + Arguments.of(position(C, TWO), position(E, FOUR), UP_RIGHT), + Arguments.of(position(F, TWO), position(C, FIVE), UP_LEFT), + Arguments.of(position(D, FOUR), position(F, TWO), DOWN_RIGHT), + Arguments.of(position(E, FOUR), position(C, TWO), DOWN_LEFT) + ); + } + + @DisplayName("방향 계산이 불가능한 출발지/목적지 위치 정보가 입력되면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenInvalidSourceAndDestinationTestCase") + @ParameterizedTest + void throwExceptionWhenInvalidSourceAndDestination(final Position source, final Position destination) { + // When & THen + assertThatThrownBy(() -> CommonMovementDirection.find(source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("방향 계산이 불가능한 거리값입니다."); + } + + private static Stream throwExceptionWhenInvalidSourceAndDestinationTestCase() { + return Stream.of( + Arguments.of(position(B, TWO), position(B, TWO)), + Arguments.of(position(B, ONE), position(C, FOUR)), + Arguments.of(position(A, EIGHT), position(G, ONE)) + ); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} diff --git a/src/test/java/domain/position/FileTest.java b/src/test/java/domain/position/FileTest.java new file mode 100644 index 00000000000..8a88ee77e0e --- /dev/null +++ b/src/test/java/domain/position/FileTest.java @@ -0,0 +1,35 @@ +package domain.position; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class FileTest { + + @DisplayName("인덱스를 입력하면 해당하는 File을 반환한다.") + @Test + void getFileTest() { + // Given + final int index = 2; + + // When + File file = File.of(index); + + // Then + assertThat(file).isEqualTo(File.C); + } + + @DisplayName("유효하지 않은 인덱스를 입력하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenInputInvalidIndex() { + // Given + final int index = 13; + + // When & Then + assertThatThrownBy(() -> File.of(index)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 인덱스입니다."); + } +} diff --git a/src/test/java/domain/position/KnightMovementDirectionTest.java b/src/test/java/domain/position/KnightMovementDirectionTest.java new file mode 100644 index 00000000000..47bab58de4c --- /dev/null +++ b/src/test/java/domain/position/KnightMovementDirectionTest.java @@ -0,0 +1,75 @@ +package domain.position; + +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 domain.position.CommonMovementDirection.DOWN_LEFT; +import static domain.position.CommonMovementDirection.DOWN_RIGHT; +import static domain.position.CommonMovementDirection.UP_LEFT; +import static domain.position.CommonMovementDirection.UP_RIGHT; +import static domain.position.File.*; +import static domain.position.KnightMovementDirection.*; +import static domain.position.Rank.*; +import static org.assertj.core.api.Assertions.assertThat; + +class KnightMovementDirectionTest { + + @DisplayName("나이트가 이동 가능한 방향의 출발지/목적지 정보가 입력되면 true를 반환한다.") + @MethodSource("isMovableDirectionTestCase") + @ParameterizedTest + void isMovableDirectionTest(final Position source, final Position destination) { + // When + boolean isMovable = isMovableDirection(source, destination); + + // Then + assertThat(isMovable).isTrue(); + } + + private static Stream isMovableDirectionTestCase() { + return Stream.of( + Arguments.of(position(C, FOUR), position(D, SIX), UP_RIGHT), + Arguments.of(position(C, FOUR), position(B, SIX), UP_LEFT), + Arguments.of(position(D, FIVE), position(E, THREE), DOWN_RIGHT), + Arguments.of(position(D, FIVE), position(C, THREE), DOWN_LEFT), + Arguments.of(position(C, FOUR), position(E, FIVE), RIGHT_UP), + Arguments.of(position(C, FOUR), position(E, THREE), RIGHT_DOWN), + Arguments.of(position(E, FOUR), position(C, FIVE), LEFT_UP), + Arguments.of(position(E, FOUR), position(C, THREE), LEFT_DOWN) + ); + } + + @DisplayName("나이트가 이동할 수 없는 출발지/도착지 위치 정보가 입력되면 false를 반환한다.") + @MethodSource("isNotMovableDirectionTestCase") + @ParameterizedTest + void isNotMovableDirectionTest(final Position source, final Position destination) { + // When + boolean isMovable = isMovableDirection(source, destination); + + // Then + assertThat(isMovable).isFalse(); + } + + private static Stream isNotMovableDirectionTestCase() { + return Stream.of( + Arguments.of(position(B, TWO), position(B, SIX)), + Arguments.of(position(B, FIVE), position(B, TWO)), + Arguments.of(position(B, TWO), position(G, TWO)), + Arguments.of(position(E, FIVE), position(B, FIVE)), + Arguments.of(position(C, TWO), position(E, FOUR)), + Arguments.of(position(F, TWO), position(C, FIVE)), + Arguments.of(position(D, FOUR), position(F, TWO)), + Arguments.of(position(E, FOUR), position(C, TWO)), + Arguments.of(position(B, TWO), position(B, TWO)), + Arguments.of(position(B, ONE), position(C, FOUR)), + Arguments.of(position(A, EIGHT), position(G, ONE)) + ); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} diff --git a/src/test/java/domain/PositionTest.java b/src/test/java/domain/position/PositionTest.java similarity index 73% rename from src/test/java/domain/PositionTest.java rename to src/test/java/domain/position/PositionTest.java index 0d181597bff..64ca031bbc9 100644 --- a/src/test/java/domain/PositionTest.java +++ b/src/test/java/domain/position/PositionTest.java @@ -1,9 +1,5 @@ -package domain; +package domain.position; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; -import domain.position.UnitVector; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,14 +21,14 @@ void createPositionTest() { assertThat(position.file()).isEqualTo(file); } - @DisplayName("백터 값을 전달하면 새로운 위치의 Position을 반환한다.") + @DisplayName("이동 방향을 전달하면 새로운 위치의 Position을 반환한다.") @Test void addPositionTest() { // Given Position position = new Position(File.D, Rank.TWO); // When - Position newPosition = position.add(UnitVector.UP_RIGHT); + Position newPosition = position.next(CommonMovementDirection.UP_RIGHT); // Then assertThat(newPosition.file()).isEqualTo(File.E); diff --git a/src/test/java/domain/position/RankTest.java b/src/test/java/domain/position/RankTest.java new file mode 100644 index 00000000000..948b3f0311f --- /dev/null +++ b/src/test/java/domain/position/RankTest.java @@ -0,0 +1,35 @@ +package domain.position; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RankTest { + + @DisplayName("인덱스를 입력하면 해당하는 Rank를 반환한다.") + @Test + void getFileTest() { + // Given + final int index = 2; + + // When + Rank file = Rank.of(index); + + // Then + assertThat(file).isEqualTo(Rank.SIX); + } + + @DisplayName("유효하지 않은 인덱스를 입력하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenInputInvalidIndex() { + // Given + final int index = 13; + + // When & Then + assertThatThrownBy(() -> Rank.of(index)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 인덱스입니다."); + } +} diff --git a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java index 4e380aeb53d..3f3632b84b4 100644 --- a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java +++ b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java @@ -1,7 +1,7 @@ package domain.strategy; import domain.position.Position; -import domain.position.UnitVector; +import domain.position.CommonMovementDirection; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -13,13 +13,13 @@ import static domain.position.File.*; import static domain.position.Rank.*; -import static domain.position.UnitVector.*; +import static domain.position.CommonMovementDirection.*; import static org.assertj.core.api.Assertions.assertThat; class ContinuousMoveStrategyTest { - private static final Set orthogonalVectors = Set.of(UP, RIGHT, DOWN, LEFT); - private static final Set diagonalVectors = Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); - private static final Set omnidirectionalVectors = Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + private static final Set orthogonalVectors = Set.of(UP, RIGHT, DOWN, LEFT); + private static final Set diagonalVectors = Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + private static final Set omnidirectionalVectors = Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); private static final ContinuousMoveStrategy orthogonalMoveStrategy = new ContinuousMoveStrategy(orthogonalVectors, 8); private static final ContinuousMoveStrategy diagonalMoveStrategy = new ContinuousMoveStrategy(diagonalVectors, 8); @@ -63,7 +63,6 @@ private static Stream orthogonalInvalidMove() { Arguments.of(new Position(D, THREE), new Position(F, THREE), Set.of(new Position(E, THREE))), Arguments.of(new Position(D, FIVE), new Position(D, TWO), Set.of(new Position(D, THREE))), Arguments.of(new Position(D, FIVE), new Position(A, FIVE), Set.of(new Position(C, FIVE))), - Arguments.of(new Position(D, FIVE), new Position(B, FOUR), Collections.emptySet()), Arguments.of(new Position(D, FIVE), new Position(B, THREE), Collections.emptySet()) ); } @@ -108,8 +107,7 @@ private static Stream diagonalInvalidMove() { Arguments.of(new Position(D, THREE), new Position(F, ONE), Set.of(new Position(E, TWO))), Arguments.of(new Position(D, FIVE), new Position(A, TWO), Set.of(new Position(B, THREE))), Arguments.of(new Position(D, FIVE), new Position(B, SEVEN), Set.of(new Position(C, SIX))), - Arguments.of(new Position(D, FIVE), new Position(H, FIVE), Collections.emptySet()), - Arguments.of(new Position(D, FIVE), new Position(F, EIGHT), Collections.emptySet()) + Arguments.of(new Position(D, FIVE), new Position(H, FIVE), Collections.emptySet()) ); } From 4db0dee7fabb1c95b58d02ff2a17c2152992a16e Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:45:51 +0900 Subject: [PATCH 34/54] =?UTF-8?q?refactor:=20direction=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=B6=94=EC=83=81=ED=99=94=20-=20directio?= =?UTF-8?q?n=20enum=EB=93=A4=EC=9D=84=20MovementDirection=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B6=94=EC=83=81=ED=99=94=20=ED=95=A8.=20-=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- .../position/CommonMovementDirection.java | 10 +-- .../position/KnightMovementDirection.java | 28 ++++++++- .../domain/position/MovementDirection.java | 8 +++ src/main/java/domain/position/Position.java | 6 +- src/main/java/domain/position/UnitVector.java | 61 ------------------- .../strategy/ContinuousMoveStrategy.java | 2 +- .../position/CommonMovementDirectionTest.java | 6 +- .../position/KnightMovementDirectionTest.java | 34 +++++------ .../java/domain/position/UnitVectorTest.java | 39 ------------ 9 files changed, 63 insertions(+), 131 deletions(-) create mode 100644 src/main/java/domain/position/MovementDirection.java delete mode 100644 src/main/java/domain/position/UnitVector.java delete mode 100644 src/test/java/domain/position/UnitVectorTest.java diff --git a/src/main/java/domain/position/CommonMovementDirection.java b/src/main/java/domain/position/CommonMovementDirection.java index df9b2565f0e..928b628da12 100644 --- a/src/main/java/domain/position/CommonMovementDirection.java +++ b/src/main/java/domain/position/CommonMovementDirection.java @@ -4,7 +4,7 @@ import java.util.Set; import java.util.function.BiPredicate; -public enum CommonMovementDirection { +public enum CommonMovementDirection implements MovementDirection { UP(-1, 0, (rowDistance, columnDistance) -> rowDistance < 0 && columnDistance == 0), DOWN(1, 0, (rowDistance, columnDistance) -> rowDistance > 0 && columnDistance == 0), RIGHT(0, 1, (rowDistance, columnDistance) -> rowDistance == 0 && columnDistance > 0), @@ -24,7 +24,7 @@ public enum CommonMovementDirection { this.condition = condition; } - public static CommonMovementDirection find(final Position source, final Position destination) { + public static CommonMovementDirection calculateDirection(final Position source, final Position destination) { final int rowDifference = destination.rowIndex() - source.rowIndex(); final int columnDifference = destination.columnIndex() - source.columnIndex(); @@ -33,14 +33,14 @@ public static CommonMovementDirection find(final Position source, final Position return Arrays.stream(CommonMovementDirection.values()) .filter(unitVector -> unitVector.condition.test(rowDifference, columnDifference)) .findAny() - .orElseThrow(() -> new IllegalArgumentException("방향 계산이 불가능한 거리값입니다.")); + .orElseThrow(() -> new IllegalArgumentException("상/하/좌/우 혹은 대각선으로 이동할 수 없는 칸입니다.")); } private static void validateDistance(final int rowDistance, final int columnDistance) { if (rowDistance == 0 && columnDistance == 0 || (!(Math.abs(rowDistance) == Math.abs(columnDistance)) && !(rowDistance == 0 || columnDistance == 0))) { - throw new IllegalArgumentException(("방향 계산이 불가능한 거리값입니다.")); + throw new IllegalArgumentException(("상/하/좌/우 혹은 대각선으로 이동할 수 없는 칸입니다.")); } } @@ -56,10 +56,12 @@ public static Set omnidirectionalVectors() { return Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); } + @Override public int getRowDistance() { return rowDistance; } + @Override public int getColumnDistance() { return columnDistance; } diff --git a/src/main/java/domain/position/KnightMovementDirection.java b/src/main/java/domain/position/KnightMovementDirection.java index b117f99a023..e021b3e2bbc 100644 --- a/src/main/java/domain/position/KnightMovementDirection.java +++ b/src/main/java/domain/position/KnightMovementDirection.java @@ -2,7 +2,7 @@ import java.util.Arrays; -public enum KnightMovementDirection { +public enum KnightMovementDirection implements MovementDirection { UP_RIGHT(-2, 1), UP_LEFT(-2, -1), DOWN_RIGHT(2, 1), @@ -20,6 +20,32 @@ public enum KnightMovementDirection { this.columnDistance = columnDistance; } + public static KnightMovementDirection calculateDirection(final Position source, final Position destination) { + final int rowDifference = destination.rowIndex() - source.rowIndex(); + final int columnDifference = destination.columnIndex() - source.columnIndex(); + + return Arrays.stream(values()) + .filter(knightMovementDirection -> knightMovementDirection.matchDistance(rowDifference, columnDifference)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("나이트가 이동할 수 없는 방향입니다.")); + } + + private boolean matchDistance(final int rowDistance, final int columnDistance) { + return this.rowDistance == rowDistance && this.columnDistance == columnDistance; + } + + @Override + public int getRowDistance() { + return rowDistance; + } + + @Override + public int getColumnDistance() { + return columnDistance; + } + + + // TODO : 레거시 public static boolean isMovableDirection(final Position source, final Position destination) { return Arrays.stream(values()) .anyMatch(knightDirection -> knightDirection.find(source, destination)); diff --git a/src/main/java/domain/position/MovementDirection.java b/src/main/java/domain/position/MovementDirection.java new file mode 100644 index 00000000000..b9626377967 --- /dev/null +++ b/src/main/java/domain/position/MovementDirection.java @@ -0,0 +1,8 @@ +package domain.position; + +public interface MovementDirection { + + int getRowDistance(); + + int getColumnDistance(); +} diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java index ba5742bf1ba..06ed49e14f9 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/position/Position.java @@ -2,9 +2,9 @@ public record Position(File file, Rank rank) { - public Position next(final CommonMovementDirection commonMovementDirection) { - File nextFile = File.of(this.columnIndex() + commonMovementDirection.getColumnDistance()); - Rank nextRank = Rank.of(this.rowIndex() + commonMovementDirection.getRowDistance()); + public Position next(final MovementDirection movementDirection) { + File nextFile = File.of(this.columnIndex() + movementDirection.getColumnDistance()); + Rank nextRank = Rank.of(this.rowIndex() + movementDirection.getRowDistance()); return new Position(nextFile, nextRank); } diff --git a/src/main/java/domain/position/UnitVector.java b/src/main/java/domain/position/UnitVector.java deleted file mode 100644 index 47e0f51390e..00000000000 --- a/src/main/java/domain/position/UnitVector.java +++ /dev/null @@ -1,61 +0,0 @@ -package domain.position; - -import java.util.Arrays; -import java.util.Set; -import java.util.function.BiPredicate; - -public enum UnitVector { - INVALID(0, 0, (row, col) -> row == 0 && col == 0), - UP(-1, 0, (row, col) -> row < 0 && col == 0), - UP_RIGHT(-1, 1, (row, col) -> row < 0 && col > 0), - RIGHT(0, 1, (row, col) -> row == 0 && col > 0), - DOWN_RIGHT(1, 1, (row, col) -> row > 0 && col > 0), - DOWN(1, 0, (row, col) -> row > 0 && col == 0), - DOWN_LEFT(1, -1, (row, col) -> row > 0 && col < 0), - LEFT(0, -1, (row, col) -> row == 0 && col < 0), - UP_LEFT(-1, -1, (row, col) -> row < 0 && col < 0); - - private final int row; - private final int col; - private final BiPredicate condition; - - UnitVector(final int row, final int col, BiPredicate condition) { - this.row = row; - this.col = col; - this.condition = condition; - } - - public static UnitVector of(final int rowDiff, final int colDiff) { - if (isInvalid(rowDiff, colDiff)) { - return INVALID; - } - return Arrays.stream(UnitVector.values()) - .filter(unitVector -> unitVector.condition.test(rowDiff, colDiff)) - .findAny() - .orElse(INVALID); - } - - private static boolean isInvalid(int rowDiff, int colDiff) { - return (rowDiff != 0 && colDiff != 0) && (Math.abs(rowDiff) != Math.abs(colDiff)); - } - - public static Set orthogonalVectors() { - return Set.of(UP, RIGHT, DOWN, LEFT); - } - - public static Set diagonalVectors() { - return Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); - } - - public static Set omnidirectionalVectors() { - return Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); - } - - public int getRow() { - return row; - } - - public int getCol() { - return col; - } -} diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java index acee4dc4027..0f54c107965 100644 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -47,6 +47,6 @@ private CommonMovementDirection findOptimalVector(final Position source, final P int rowDiff = destination.rowIndex() - source.rowIndex(); int colDiff = destination.columnIndex() - source.columnIndex(); - return CommonMovementDirection.find(source, destination); + return CommonMovementDirection.calculateDirection(source, destination); } } diff --git a/src/test/java/domain/position/CommonMovementDirectionTest.java b/src/test/java/domain/position/CommonMovementDirectionTest.java index dbe21b0711a..26776a306bd 100644 --- a/src/test/java/domain/position/CommonMovementDirectionTest.java +++ b/src/test/java/domain/position/CommonMovementDirectionTest.java @@ -20,7 +20,7 @@ class CommonMovementDirectionTest { @ParameterizedTest void findMovementDirection(final Position source, final Position destination, final CommonMovementDirection expect) { // When - CommonMovementDirection commonMovementDirection = CommonMovementDirection.find(source, destination); + CommonMovementDirection commonMovementDirection = CommonMovementDirection.calculateDirection(source, destination); // Then assertThat(commonMovementDirection).isEqualTo(expect); @@ -44,9 +44,9 @@ private static Stream findMovementDirectionTestCase() { @ParameterizedTest void throwExceptionWhenInvalidSourceAndDestination(final Position source, final Position destination) { // When & THen - assertThatThrownBy(() -> CommonMovementDirection.find(source, destination)) + assertThatThrownBy(() -> CommonMovementDirection.calculateDirection(source, destination)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("방향 계산이 불가능한 거리값입니다."); + .hasMessage("상/하/좌/우 혹은 대각선으로 이동할 수 없는 칸입니다."); } private static Stream throwExceptionWhenInvalidSourceAndDestinationTestCase() { diff --git a/src/test/java/domain/position/KnightMovementDirectionTest.java b/src/test/java/domain/position/KnightMovementDirectionTest.java index 47bab58de4c..45bc34333fc 100644 --- a/src/test/java/domain/position/KnightMovementDirectionTest.java +++ b/src/test/java/domain/position/KnightMovementDirectionTest.java @@ -7,29 +7,26 @@ import java.util.stream.Stream; -import static domain.position.CommonMovementDirection.DOWN_LEFT; -import static domain.position.CommonMovementDirection.DOWN_RIGHT; -import static domain.position.CommonMovementDirection.UP_LEFT; -import static domain.position.CommonMovementDirection.UP_RIGHT; import static domain.position.File.*; import static domain.position.KnightMovementDirection.*; import static domain.position.Rank.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class KnightMovementDirectionTest { - @DisplayName("나이트가 이동 가능한 방향의 출발지/목적지 정보가 입력되면 true를 반환한다.") - @MethodSource("isMovableDirectionTestCase") + @DisplayName("입력된 출발지 & 목적지를 계산해서 나이트의 이동 방향을 반환한다.") + @MethodSource("calculateDirectionTestCase") @ParameterizedTest - void isMovableDirectionTest(final Position source, final Position destination) { + void calculateDirectionTest(final Position source, final Position destination, final KnightMovementDirection expect) { // When - boolean isMovable = isMovableDirection(source, destination); + KnightMovementDirection knightMovementDirection = calculateDirection(source, destination); // Then - assertThat(isMovable).isTrue(); + assertThat(knightMovementDirection).isEqualTo(expect); } - private static Stream isMovableDirectionTestCase() { + private static Stream calculateDirectionTestCase() { return Stream.of( Arguments.of(position(C, FOUR), position(D, SIX), UP_RIGHT), Arguments.of(position(C, FOUR), position(B, SIX), UP_LEFT), @@ -42,18 +39,17 @@ private static Stream isMovableDirectionTestCase() { ); } - @DisplayName("나이트가 이동할 수 없는 출발지/도착지 위치 정보가 입력되면 false를 반환한다.") - @MethodSource("isNotMovableDirectionTestCase") + @DisplayName("나이트가 이동할 수 없는 출발지/도착지 위치 정보가 입력되면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenInvalidLocationTestCase") @ParameterizedTest - void isNotMovableDirectionTest(final Position source, final Position destination) { - // When - boolean isMovable = isMovableDirection(source, destination); - - // Then - assertThat(isMovable).isFalse(); + void throwExceptionWhenInvalidLocationTest(final Position source, final Position destination) { + // When & Then + assertThatThrownBy(() -> KnightMovementDirection.calculateDirection(source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("나이트가 이동할 수 없는 방향입니다."); } - private static Stream isNotMovableDirectionTestCase() { + private static Stream throwExceptionWhenInvalidLocationTestCase() { return Stream.of( Arguments.of(position(B, TWO), position(B, SIX)), Arguments.of(position(B, FIVE), position(B, TWO)), diff --git a/src/test/java/domain/position/UnitVectorTest.java b/src/test/java/domain/position/UnitVectorTest.java deleted file mode 100644 index 84bc80f7c5e..00000000000 --- a/src/test/java/domain/position/UnitVectorTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package domain.position; - -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; - -class UnitVectorTest { - @DisplayName("출발지와 목적지의 거리차에 따라 올바른 이동 방향을 반환한다.") - @MethodSource("testSource") - @ParameterizedTest - void findUnitVectorTest(int rowDiff, int colDiff, UnitVector expected) { - // When - UnitVector unitVector = UnitVector.of(rowDiff, colDiff); - - // Then - assertThat(unitVector).isEqualTo(expected); - } - - private static Stream testSource() { - return Stream.of( - Arguments.of(0, 0, UnitVector.INVALID), - Arguments.of(-2, 0, UnitVector.UP), - Arguments.of(-7, 7, UnitVector.UP_RIGHT), - Arguments.of(0, 5, UnitVector.RIGHT), - Arguments.of(3, 3, UnitVector.DOWN_RIGHT), - Arguments.of(4, 0, UnitVector.DOWN), - Arguments.of(6, -6, UnitVector.DOWN_LEFT), - Arguments.of(0, -7, UnitVector.LEFT), - Arguments.of(-3, -3, UnitVector.UP_LEFT), - Arguments.of(2, 3, UnitVector.INVALID), - Arguments.of(-2, 1, UnitVector.INVALID) - ); - } -} From 5a29fd593512ea77dbe115be845b02359f343be0 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:46:09 +0900 Subject: [PATCH 35/54] =?UTF-8?q?feat:=20Bishop=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu --- src/main/java/domain/piece/Bishop.java | 66 +++++++++++++ src/test/java/domain/piece/BishopTest.java | 105 +++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/main/java/domain/piece/Bishop.java create mode 100644 src/test/java/domain/piece/BishopTest.java diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java new file mode 100644 index 00000000000..d55731b6a20 --- /dev/null +++ b/src/main/java/domain/piece/Bishop.java @@ -0,0 +1,66 @@ +package domain.piece; + +import domain.position.CommonMovementDirection; +import domain.position.Position; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static domain.position.CommonMovementDirection.*; + +public class Bishop extends Piece { + private static final List MOVABLE_DIRECTIONS = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); + + public Bishop(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + CommonMovementDirection movementDirection = calculateDirection(source, destination); + validateMovementDirection(movementDirection); + + Position alivePosition = move(source, destination, movementDirection, piecePositions); + + checkAlivePosition(alivePosition, piecePositions); + } + + private void validateMovementDirection(final CommonMovementDirection movementDirection) { + if (!MOVABLE_DIRECTIONS.contains(movementDirection)) { + throw new IllegalArgumentException("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + } + } + + private Position move( + final Position source, + final Position destination, + final CommonMovementDirection movementDirection, + final Map piecePositions + ) { + List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) + .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .toList(); + + return movePaths.get(movePaths.size() - 1) + .next(movementDirection); + } + + private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + if (current.equals(destination)) { + return false; + } + + if (piecePositions.containsKey(current)) { + throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + return true; + } + + private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { + if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + } +} diff --git a/src/test/java/domain/piece/BishopTest.java b/src/test/java/domain/piece/BishopTest.java new file mode 100644 index 00000000000..2c05fdfd27f --- /dev/null +++ b/src/test/java/domain/piece/BishopTest.java @@ -0,0 +1,105 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.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.Collections; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BishopTest { + + @DisplayName("주어진 출발지 -> 도착지를 Bishop이 이동할 수 있는지 검증한다.") + @MethodSource("checkMovableTestCase") + @ParameterizedTest + void checkMovableTest(final Position source, final Position destination) { + // Given + Bishop bishop = new Bishop(PieceColor.WHITE); + Map piecePositions = Map.of(position(File.C, Rank.ONE), new Rook(PieceColor.BLACK)); + + // When & Then + assertThatCode(() -> bishop.checkMovable(source, destination, piecePositions)) + .doesNotThrowAnyException(); + } + + private static Stream checkMovableTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.E, Rank.FIVE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.THREE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.C, Rank.ONE)) + ); + } + + @DisplayName("Bishop이 이동할 수 없는 방향의 도착지가 입력되면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenInvalidDirectionTestCase") + @ParameterizedTest + void throwExceptionWhenInvalidDirectionTest(final Position source, final Position destination) { + // Given + Bishop bishop = new Bishop(PieceColor.WHITE); + Map piecePositions = Collections.emptyMap(); + + // When & Then + assertThatThrownBy(() ->bishop.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + } + + private static Stream throwExceptionWhenInvalidDirectionTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.FIVE)), + Arguments.of(position(File.B, Rank.TWO), position(File.G, Rank.TWO)) + ); + } + + @DisplayName("이동 경로에 기물이 존재하면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenPathsHasPieceTestCase") + @ParameterizedTest + void throwExceptionWhenPathsHasPieceTest(final Position source, final Position destination) { + // Given + Bishop bishop = new Bishop(PieceColor.WHITE); + Map piecePositions = Map.of( + position(File.C, Rank.THREE), new Bishop(PieceColor.BLACK) + ); + + // When & Then + assertThatThrownBy(() -> bishop.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + private static Stream throwExceptionWhenPathsHasPieceTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.E, Rank.FIVE)), + Arguments.of(position(File.D, Rank.FOUR), position(File.B, Rank.TWO)) + ); + } + + @DisplayName("도착지에 아군 기물이 존재하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDestinationHasTeamPieceTest() { + // Given + Position source = position(File.B, Rank.TWO); + Position destination = position(File.D, Rank.FOUR); + Bishop bishop = new Bishop(PieceColor.WHITE); + Map piecePositions = Map.of(destination, new Bishop(PieceColor.WHITE)); + + // When & Then + assertThatThrownBy(() -> bishop.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From 1d3217d1a3c20237944f5c9e68590b0c66154abd Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:46:31 +0900 Subject: [PATCH 36/54] =?UTF-8?q?feat:=20Rook=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-현by: Hyunguk Ryu --- src/main/java/domain/piece/Rook.java | 66 ++++++++++++++ src/test/java/domain/piece/RookTest.java | 106 +++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 src/main/java/domain/piece/Rook.java create mode 100644 src/test/java/domain/piece/RookTest.java diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java new file mode 100644 index 00000000000..66c7610d1d7 --- /dev/null +++ b/src/main/java/domain/piece/Rook.java @@ -0,0 +1,66 @@ +package domain.piece; + +import domain.position.CommonMovementDirection; +import domain.position.Position; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static domain.position.CommonMovementDirection.*; + +public class Rook extends Piece { + private static final List MOVABLE_DIRECTIONS = List.of(UP, DOWN, RIGHT, LEFT); + + public Rook(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + CommonMovementDirection movementDirection = calculateDirection(source, destination); + validateMovementDirection(movementDirection); + + Position alivePosition = move(source, destination, movementDirection, piecePositions); + + checkAlivePosition(alivePosition, piecePositions); + } + + private void validateMovementDirection(final CommonMovementDirection movementDirection) { + if (!MOVABLE_DIRECTIONS.contains(movementDirection)) { + throw new IllegalArgumentException("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + } + } + + private Position move( + final Position source, + final Position destination, + final CommonMovementDirection movementDirection, + final Map piecePositions + ) { + List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) + .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .toList(); + + return movePaths.get(movePaths.size() - 1) + .next(movementDirection); + } + + private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + if (current.equals(destination)) { + return false; + } + + if (piecePositions.containsKey(current)) { + throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + return true; + } + + private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { + if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + } +} diff --git a/src/test/java/domain/piece/RookTest.java b/src/test/java/domain/piece/RookTest.java new file mode 100644 index 00000000000..2889cf6b175 --- /dev/null +++ b/src/test/java/domain/piece/RookTest.java @@ -0,0 +1,106 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.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.Collections; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RookTest { + + @DisplayName("주어진 출발지 -> 도착지를 Rook이 이동할 수 있는지 검증한다.") + @MethodSource("checkMovableTestCase") + @ParameterizedTest + void checkMovableTest(final Position source, final Position destination) { + // Given + Rook rook = new Rook(PieceColor.WHITE); + Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + + // When & Then + assertThatCode(() -> rook.checkMovable(source, destination, piecePositions)) + .doesNotThrowAnyException(); + } + + private static Stream checkMovableTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.SIX)), + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.TWO)), + Arguments.of(position(File.B, Rank.TWO), position(File.D, Rank.TWO)) + ); + } + + @DisplayName("Rook이 이동할 수 없는 방향의 도착지가 입력되면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenInvalidDirectionTestCase") + @ParameterizedTest + void throwExceptionWhenInvalidDirectionTest(final Position source, final Position destination) { + // Given + Rook rook = new Rook(PieceColor.WHITE); + Map piecePositions = Collections.emptyMap(); + + // When & Then + assertThatThrownBy(() ->rook.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + } + + private static Stream throwExceptionWhenInvalidDirectionTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.E, Rank.FIVE)), + Arguments.of(position(File.B, Rank.TWO), position(File.G, Rank.SEVEN)) + ); + } + + @DisplayName("이동 경로에 기물이 존재하면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenPathsHasPieceTestCase") + @ParameterizedTest + void throwExceptionWhenPathsHasPieceTest(final Position source, final Position destination) { + // Given + Rook rook = new Rook(PieceColor.WHITE); + Map piecePositions = Map.of( + position(File.B, Rank.FOUR), new Rook(PieceColor.BLACK), + position(File.D, Rank.TWO), new Rook(PieceColor.WHITE) + ); + + // When & Then + assertThatThrownBy(() -> rook.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + private static Stream throwExceptionWhenPathsHasPieceTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.SIX)), + Arguments.of(position(File.B, Rank.TWO), position(File.G, Rank.TWO)) + ); + } + + @DisplayName("도착지에 아군 기물이 존재하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDestinationHasTeamPieceTest() { + // Given + Position source = position(File.B, Rank.TWO); + Position destination = position(File.B, Rank.SIX); + Rook rook = new Rook(PieceColor.WHITE); + Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + + // When & Then + assertThatThrownBy(() -> rook.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From a99b95b105ec3c94641cce62f9655961341663fb Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:46:49 +0900 Subject: [PATCH 37/54] =?UTF-8?q?feat:=20King=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authore현d-by: Hyunguk Ryu --- src/main/java/domain/piece/King.java | 40 ++++++++++++ src/test/java/domain/piece/KingTest.java | 80 ++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/main/java/domain/piece/King.java create mode 100644 src/test/java/domain/piece/KingTest.java diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java new file mode 100644 index 00000000000..28d4c3ebb32 --- /dev/null +++ b/src/main/java/domain/piece/King.java @@ -0,0 +1,40 @@ +package domain.piece; + +import domain.position.CommonMovementDirection; +import domain.position.Position; + +import java.util.Map; + +import static domain.position.CommonMovementDirection.calculateDirection; + +public class King extends Piece { + + public King(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + CommonMovementDirection movementDirection = calculateDirection(source, destination); + checkMoveDistance(source, destination, movementDirection); + + Position alivePosition = source.next(movementDirection); + + checkAlivePosition(alivePosition, piecePositions); + } + + private void checkMoveDistance(final Position source, final Position destination, final CommonMovementDirection movementDirection) { + int rowDistance = destination.rowIndex() - source.rowIndex(); + int columnDistance = destination.columnIndex() - source.columnIndex(); + + if (movementDirection.getRowDistance() != rowDistance || movementDirection.getColumnDistance() != columnDistance) { + throw new IllegalArgumentException("이동할 수 없는 거리입니다."); + } + } + + private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { + if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + } +} diff --git a/src/test/java/domain/piece/KingTest.java b/src/test/java/domain/piece/KingTest.java new file mode 100644 index 00000000000..ee0bc874404 --- /dev/null +++ b/src/test/java/domain/piece/KingTest.java @@ -0,0 +1,80 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.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.Collections; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class KingTest { + + @DisplayName("주어진 출발지 -> 도착지를 Rook이 이동할 수 있는지 검증한다.") + @MethodSource("checkMovableTestCase") + @ParameterizedTest + void checkMovableTest(final Position source, final Position destination) { + // Given + King king = new King(PieceColor.WHITE); + Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + + // When & Then + assertThatCode(() -> king.checkMovable(source, destination, piecePositions)) + .doesNotThrowAnyException(); + } + + private static Stream checkMovableTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.THREE)), + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.TWO)), + Arguments.of(position(File.B, Rank.TWO), position(File.C, Rank.TWO)), + Arguments.of(position(File.B, Rank.TWO), position(File.C, Rank.THREE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.THREE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.C, Rank.ONE)) + ); + } + + @DisplayName("출발지에서 목적지까지 거리가 두 칸 이상이면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDistanceOverOrEqualTwo() { + // Given + Position source = position(File.B, Rank.THREE); + Position destination = position(File.B, Rank.SIX); + King king = new King(PieceColor.WHITE); + Map piecePositions = Collections.emptyMap(); + + // When & Then + assertThatThrownBy(() -> king.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이동할 수 없는 거리입니다."); + } + + @DisplayName("도착지에 아군 기물이 존재하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDestinationHasTeamPieceTest() { + // Given + Position source = position(File.B, Rank.TWO); + Position destination = position(File.B, Rank.THREE); + King king = new King(PieceColor.WHITE); + Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + + // When & Then + assertThatThrownBy(() -> king.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From 6b75092ab4d39738afab01b1149516e1e6ab6d8c Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:47:56 +0900 Subject: [PATCH 38/54] =?UTF-8?q?feat:=20Queen=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authore현d-by: Hyunguk Ryu --- src/main/java/domain/piece/Piece.java | 23 ++++++ src/main/java/domain/piece/PieceColor.java | 5 ++ src/main/java/domain/piece/Queen.java | 58 ++++++++++++++ src/test/java/domain/piece/QueenTest.java | 88 ++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 src/main/java/domain/piece/Piece.java create mode 100644 src/main/java/domain/piece/PieceColor.java create mode 100644 src/main/java/domain/piece/Queen.java create mode 100644 src/test/java/domain/piece/QueenTest.java diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java new file mode 100644 index 00000000000..26245eb1fb0 --- /dev/null +++ b/src/main/java/domain/piece/Piece.java @@ -0,0 +1,23 @@ +package domain.piece; + +import domain.position.Position; + +import java.util.Map; + +public abstract class Piece { + private final PieceColor color; + + protected Piece(final PieceColor color) { + this.color = color; + } + + public abstract void checkMovable(final Position source, final Position destination, final Map piecePositions); + + protected boolean checkEnemy(final Piece otherPiece) { + return otherPiece.isEnemy(this.color); + } + + public boolean isEnemy(final PieceColor otherColor) { + return this.color != otherColor; + } +} diff --git a/src/main/java/domain/piece/PieceColor.java b/src/main/java/domain/piece/PieceColor.java new file mode 100644 index 00000000000..40fccd46e06 --- /dev/null +++ b/src/main/java/domain/piece/PieceColor.java @@ -0,0 +1,5 @@ +package domain.piece; + +public enum PieceColor { + BLACK, WHITE +} diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java new file mode 100644 index 00000000000..fe6d3707e27 --- /dev/null +++ b/src/main/java/domain/piece/Queen.java @@ -0,0 +1,58 @@ +package domain.piece; + +import domain.position.CommonMovementDirection; +import domain.position.Position; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static domain.position.CommonMovementDirection.*; + +public class Queen extends Piece { + + public Queen(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + CommonMovementDirection movementDirection = calculateDirection(source, destination); + + Position alivePosition = move(source, destination, movementDirection, piecePositions); + + checkAlivePosition(alivePosition, piecePositions); + } + + private Position move( + final Position source, + final Position destination, + final CommonMovementDirection movementDirection, + final Map piecePositions + ) { + List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) + .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .toList(); + + return movePaths.get(movePaths.size() - 1) + .next(movementDirection); + } + + private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + if (current.equals(destination)) { + return false; + } + + if (piecePositions.containsKey(current)) { + throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + return true; + } + + private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { + if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + } +} diff --git a/src/test/java/domain/piece/QueenTest.java b/src/test/java/domain/piece/QueenTest.java new file mode 100644 index 00000000000..78ec2b1dec2 --- /dev/null +++ b/src/test/java/domain/piece/QueenTest.java @@ -0,0 +1,88 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.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.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class QueenTest { + + @DisplayName("주어진 출발지 -> 도착지를 Rook이 이동할 수 있는지 검증한다.") + @MethodSource("checkMovableTestCase") + @ParameterizedTest + void checkMovableTest(final Position source, final Position destination) { + // Given + Queen queen = new Queen(PieceColor.WHITE); + Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + + // When & Then + assertThatCode(() -> queen.checkMovable(source, destination, piecePositions)) + .doesNotThrowAnyException(); + } + + private static Stream checkMovableTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.SIX)), + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.TWO)), + Arguments.of(position(File.B, Rank.TWO), position(File.D, Rank.TWO)), + Arguments.of(position(File.B, Rank.TWO), position(File.E, Rank.FIVE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.THREE)), + Arguments.of(position(File.B, Rank.TWO), position(File.A, Rank.ONE)), + Arguments.of(position(File.B, Rank.TWO), position(File.C, Rank.ONE)) + ); + } + + @DisplayName("이동 경로에 기물이 존재하면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenPathsHasPieceTestCase") + @ParameterizedTest + void throwExceptionWhenPathsHasPieceTest(final Position source, final Position destination) { + // Given + Queen queen = new Queen(PieceColor.WHITE); + Map piecePositions = Map.of( + position(File.B, Rank.FOUR), new Bishop(PieceColor.BLACK), + position(File.D, Rank.TWO), new Rook(PieceColor.WHITE) + ); + + // When & Then + assertThatThrownBy(() -> queen.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); + } + + private static Stream throwExceptionWhenPathsHasPieceTestCase() { + return Stream.of( + Arguments.of(position(File.B, Rank.TWO), position(File.B, Rank.SIX)), + Arguments.of(position(File.B, Rank.TWO), position(File.G, Rank.TWO)) + ); + } + + @DisplayName("도착지에 아군 기물이 존재하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDestinationHasTeamPieceTest() { + // Given + Position source = position(File.B, Rank.TWO); + Position destination = position(File.B, Rank.SIX); + Queen queen = new Queen(PieceColor.WHITE); + Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + + // When & Then + assertThatThrownBy(() -> queen.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From d8af75b9b01f8a17b07f9a8f946b67c8202ca45c Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 20:48:24 +0900 Subject: [PATCH 39/54] =?UTF-8?q?feat:=20Knight=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/domain/piece/Knight.java | 30 ++++++++++++++++++++++ src/test/java/domain/piece/KnightTest.java | 29 +++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/main/java/domain/piece/Knight.java create mode 100644 src/test/java/domain/piece/KnightTest.java diff --git a/src/main/java/domain/piece/Knight.java b/src/main/java/domain/piece/Knight.java new file mode 100644 index 00000000000..690b5d6a55c --- /dev/null +++ b/src/main/java/domain/piece/Knight.java @@ -0,0 +1,30 @@ +package domain.piece; + +import domain.position.KnightMovementDirection; +import domain.position.Position; + +import java.util.Map; + +import static domain.position.KnightMovementDirection.calculateDirection; + +public class Knight extends Piece { + + public Knight(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + KnightMovementDirection movementDirection = calculateDirection(source, destination); + + Position alivePosition = source.next(movementDirection); + + checkAlivePosition(alivePosition, piecePositions); + } + + private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { + if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } + } +} diff --git a/src/test/java/domain/piece/KnightTest.java b/src/test/java/domain/piece/KnightTest.java new file mode 100644 index 00000000000..ba63ffb1717 --- /dev/null +++ b/src/test/java/domain/piece/KnightTest.java @@ -0,0 +1,29 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class KnightTest { + + @DisplayName("도착지에 아군 기물이 존재하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenDestinationHasTeamPieceTest() { + // Given + Position source = new Position(File.C, Rank.FOUR); + Position destination = new Position(File.D, Rank.SIX); + Knight knight = new Knight(PieceColor.WHITE); + Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + + // When & Then + assertThatThrownBy(() -> knight.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); + } +} From d2a273e217af0074dfb918f2f752006304fe5d3c Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sat, 23 Mar 2024 22:51:41 +0900 Subject: [PATCH 40/54] =?UTF-8?q?feat:=20Pawn=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-현by: Hyunguk Ryu --- src/main/java/domain/piece/Pawn.java | 64 +++++++++ src/main/java/domain/piece/Piece.java | 2 +- .../position/PawnMovementDirection.java | 83 ++++++++++++ src/test/java/domain/piece/PawnTest.java | 127 ++++++++++++++++++ .../position/PawnMovementDirectionTest.java | 77 +++++++++++ 5 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 src/main/java/domain/piece/Pawn.java create mode 100644 src/main/java/domain/position/PawnMovementDirection.java create mode 100644 src/test/java/domain/piece/PawnTest.java create mode 100644 src/test/java/domain/position/PawnMovementDirectionTest.java diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java new file mode 100644 index 00000000000..24ed167fd43 --- /dev/null +++ b/src/main/java/domain/piece/Pawn.java @@ -0,0 +1,64 @@ +package domain.piece; + +import domain.position.PawnMovementDirection; +import domain.position.Position; + +import java.util.Map; + +import static domain.piece.PieceColor.BLACK; +import static domain.piece.PieceColor.WHITE; +import static domain.position.PawnMovementDirection.calculateDirection; +import static domain.position.Rank.SEVEN; +import static domain.position.Rank.TWO; + +public class Pawn extends Piece { + + public Pawn(final PieceColor color) { + super(color); + } + + @Override + public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + PawnMovementDirection movementDirection = calculateDirection(color, source, destination); + if (movementDirection.isCrossStep()) { + checkAlivePositionOfCrossStep(source.next(movementDirection), piecePositions); + return; + } + + if (movementDirection.isTwoStep()) { + checkIsStartPosition(source); + } + checkMovePaths(source, movementDirection, piecePositions); + } + + private void checkAlivePositionOfCrossStep(final Position alivePosition, final Map piecePositions) { + if (!piecePositions.containsKey(alivePosition) || !checkEnemy(piecePositions.get(alivePosition))) { + throw new IllegalArgumentException("적 기물이 존재하지 않으면 대각선으로 이동할 수 없습니다"); + } + } + + private void checkIsStartPosition(final Position source) { + if ((color == WHITE && source.rank() != TWO) || (color == BLACK && source.rank() != SEVEN)) { + throw new IllegalArgumentException("시작 위치가 아니면 두 칸 이동할 수 없습니다."); + } + } + + private void checkMovePaths( + final Position source, + final PawnMovementDirection movementDirection, + final Map piecePositions + ) { + Position current = source; + int moveDistance = Math.abs(movementDirection.getRowDistance()); + for (int i = 0; i < moveDistance; i++) { + current = current.next(movementDirection.convertOneStep()); + checkPathHasPiece(current, piecePositions); + } + } + + private void checkPathHasPiece(final Position path, final Map piecePositions) { + if (piecePositions.containsKey(path)) { + throw new IllegalArgumentException("기물이 존재하는 칸으로 이동할 수 없습니다."); + } + } +} diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index 26245eb1fb0..71f086bc88d 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -5,7 +5,7 @@ import java.util.Map; public abstract class Piece { - private final PieceColor color; + protected final PieceColor color; protected Piece(final PieceColor color) { this.color = color; diff --git a/src/main/java/domain/position/PawnMovementDirection.java b/src/main/java/domain/position/PawnMovementDirection.java new file mode 100644 index 00000000000..54d349ca276 --- /dev/null +++ b/src/main/java/domain/position/PawnMovementDirection.java @@ -0,0 +1,83 @@ +package domain.position; + +import domain.piece.PieceColor; + +import java.util.Arrays; + +import static domain.piece.PieceColor.*; + +public enum PawnMovementDirection implements MovementDirection { + UP_ONE_STEP(-1, 0, WHITE), + UP_TWO_STEP(-2, 0, WHITE), + UP_RIGHT(-1, 1, WHITE), + UP_LEFT(-1, -1, WHITE), + DOWN_ONE_STEP(1, 0, BLACK), + DOWN_TWO_STEP(2, 0, BLACK), + DOWN_RIGHT(1, 1, BLACK), + DOWN_LEFT(1, -1, BLACK); + + private final int rowDistance; + private final int columnDistance; + private final PieceColor pieceColor; + + PawnMovementDirection(final int rowDistance, final int columnDistance, final PieceColor pieceColor) { + this.rowDistance = rowDistance; + this.columnDistance = columnDistance; + this.pieceColor = pieceColor; + } + + public static PawnMovementDirection calculateDirection(final PieceColor pieceColor, final Position source, final Position destination) { + final int rowDifference = destination.rowIndex() - source.rowIndex(); + final int columnDifference = destination.columnIndex() - source.columnIndex(); + + return Arrays.stream(values()) + .filter(knightMovementDirection -> knightMovementDirection.matchDistance(pieceColor, rowDifference, columnDifference)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(pieceColor.name() + "색상의 폰이 이동할 수 없는 방향입니다.")); + } + + private boolean matchDistance(final PieceColor pieceColor, final int rowDistance, final int columnDistance) { + return this.pieceColor == pieceColor + && this.rowDistance == rowDistance + && this.columnDistance == columnDistance; + } + + public boolean isCrossStep() { + return this == UP_RIGHT || this == UP_LEFT + || this == DOWN_RIGHT || this == DOWN_LEFT; + } + + public boolean isTwoStep() { + return this == UP_TWO_STEP || this == DOWN_TWO_STEP; + } + + public PawnMovementDirection convertOneStep() { + if (!this.isTwoStep() && !this.isOneStep()) { + throw new IllegalArgumentException("전진 방향만 변환할 수 있습니다."); + } + + if (this == UP_TWO_STEP) { + return UP_ONE_STEP; + } + + if (this == DOWN_TWO_STEP) { + return DOWN_ONE_STEP; + } + + return this; + } + + private boolean isOneStep() { + return this == UP_ONE_STEP || this == DOWN_ONE_STEP; + } + + @Override + public int getRowDistance() { + return rowDistance; + } + + @Override + public int getColumnDistance() { + return columnDistance; + } +} diff --git a/src/test/java/domain/piece/PawnTest.java b/src/test/java/domain/piece/PawnTest.java new file mode 100644 index 00000000000..65bdde31bbe --- /dev/null +++ b/src/test/java/domain/piece/PawnTest.java @@ -0,0 +1,127 @@ +package domain.piece; + +import domain.position.File; +import domain.position.Position; +import domain.position.Rank; +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.Collections; +import java.util.Map; +import java.util.stream.Stream; + +import static domain.piece.PieceColor.BLACK; +import static domain.piece.PieceColor.WHITE; +import static domain.position.File.*; +import static domain.position.PawnMovementDirection.*; +import static domain.position.Rank.*; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PawnTest { + + @DisplayName("주어진 출발지 -> 도착지를 Pawn이 이동할 수 있는지 검증한다.") + @MethodSource("checkMovableTestCase") + @ParameterizedTest + void checkMovableTest(final PieceColor pieceColor, final PieceColor enemyColor, final Position source, final Position destination) { + // Given + Pawn pawn = new Pawn(pieceColor); + Map piecePositions = Map.of( + position(E, SIX), new Rook(enemyColor), + position(C, SIX), new Rook(enemyColor), + position(E, FOUR), new Rook(enemyColor), + position(C, FOUR), new Rook(enemyColor) + ); + + // When & Then + assertThatCode(() -> pawn.checkMovable(source, destination, piecePositions)) + .doesNotThrowAnyException(); + } + + private static Stream checkMovableTestCase() { + return Stream.of( + Arguments.of(WHITE, BLACK, position(F, TWO), position(F, FOUR), UP_TWO_STEP), + Arguments.of(WHITE, BLACK, position(C, FOUR), position(C, FIVE), UP_ONE_STEP), + Arguments.of(WHITE, BLACK, position(D, FIVE), position(E, SIX), UP_RIGHT), + Arguments.of(WHITE, BLACK, position(D, FIVE), position(C, SIX), UP_LEFT), + Arguments.of(BLACK, WHITE, position(G, SEVEN), position(G, FIVE), DOWN_TWO_STEP), + Arguments.of(BLACK, WHITE, position(C, FOUR), position(C, THREE), DOWN_ONE_STEP), + Arguments.of(BLACK, WHITE, position(D, FIVE), position(E, FOUR), DOWN_RIGHT), + Arguments.of(BLACK, WHITE, position(D, FIVE), position(C, FOUR), DOWN_LEFT) + ); + } + + @DisplayName("대각석으로 이동할 경우 목적지에 적 기물이 존재하지 않으면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenCrossStepDestinationNotHasEnemyTestCase") + @ParameterizedTest + void throwExceptionWhenCrossStepDestinationNotHasEnemyTest(final PieceColor pieceColor, final Position source, final Position destination) { + // Given + Pawn pawn = new Pawn(pieceColor); + Map piecePositions = Collections.emptyMap(); + + // When & Then + assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("적 기물이 존재하지 않으면 대각선으로 이동할 수 없습니다"); + } + + private static Stream throwExceptionWhenCrossStepDestinationNotHasEnemyTestCase() { + return Stream.of( + Arguments.of(WHITE, position(D, FIVE), position(E, SIX), UP_RIGHT), + Arguments.of(WHITE, position(D, FIVE), position(C, SIX), UP_LEFT), + Arguments.of(BLACK, position(D, FIVE), position(E, FOUR), DOWN_RIGHT), + Arguments.of(BLACK, position(D, FIVE), position(C, FOUR), DOWN_LEFT) + ); + } + + @DisplayName("두 칸 전진할 경우 출발지가 폰의 시작 위치가 아니면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenOneStepSourceIsNotStartPositionTestCase") + @ParameterizedTest + void throwExceptionWhenOneStepSourceIsNotStartPositionTest(final PieceColor pieceColor, final Position source, final Position destination) { + // Given + Pawn pawn = new Pawn(pieceColor); + Map piecePositions = Collections.emptyMap(); + + // When & Then + assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("시작 위치가 아니면 두 칸 이동할 수 없습니다."); + } + + private static Stream throwExceptionWhenOneStepSourceIsNotStartPositionTestCase() { + return Stream.of( + Arguments.of(WHITE, position(C, THREE), position(C, FIVE)), + Arguments.of(BLACK, position(C, FIVE), position(C, THREE)) + ); + } + + @DisplayName("폰이 전진할 시 경로에 기물이 존재하면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenForwardPathHasPieceTestCase") + @ParameterizedTest + void throwExceptionWhenForwardPathHasPieceTest(final PieceColor pieceColor, final Position source, final Position destination) { + // Given + Pawn pawn = new Pawn(pieceColor); + Map piecePositions = Map.of( + position(D, THREE), new Rook(WHITE), + position(C, THREE), new Rook(BLACK) + ); + + // When & Then + assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("기물이 존재하는 칸으로 이동할 수 없습니다."); + } + + private static Stream throwExceptionWhenForwardPathHasPieceTestCase() { + return Stream.of( + Arguments.of(WHITE, position(D, TWO), position(D, FOUR)), + Arguments.of(BLACK, position(C, FOUR), position(C, THREE)) + ); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} diff --git a/src/test/java/domain/position/PawnMovementDirectionTest.java b/src/test/java/domain/position/PawnMovementDirectionTest.java new file mode 100644 index 00000000000..02c6e9a6bf6 --- /dev/null +++ b/src/test/java/domain/position/PawnMovementDirectionTest.java @@ -0,0 +1,77 @@ +package domain.position; + +import domain.piece.PieceColor; +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 domain.piece.PieceColor.*; +import static domain.position.File.*; +import static domain.position.PawnMovementDirection.*; +import static domain.position.Rank.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PawnMovementDirectionTest { + + @DisplayName("입력된 출발지 & 목적지를 계산해서 폰의 이동 방향을 반환한다.") + @MethodSource("calculateDirectionTestCase") + @ParameterizedTest + void calculateDirectionTest(final PieceColor pieceColor, final Position source, final Position destination, final PawnMovementDirection expect) { + // When + PawnMovementDirection movementDirection = calculateDirection(pieceColor, source, destination); + + // Then + assertThat(movementDirection).isEqualTo(expect); + } + + private static Stream calculateDirectionTestCase() { + return Stream.of( + Arguments.of(WHITE, position(C, TWO), position(C, FOUR), UP_TWO_STEP), + Arguments.of(WHITE, position(C, FOUR), position(C, FIVE), UP_ONE_STEP), + Arguments.of(WHITE, position(D, FIVE), position(E, SIX), UP_RIGHT), + Arguments.of(WHITE, position(D, FIVE), position(C, SIX), UP_LEFT), + Arguments.of(BLACK, position(C, SEVEN), position(C, FIVE), DOWN_TWO_STEP), + Arguments.of(BLACK, position(C, FOUR), position(C, THREE), DOWN_ONE_STEP), + Arguments.of(BLACK, position(D, FIVE), position(E, FOUR), DOWN_RIGHT), + Arguments.of(BLACK, position(D, FIVE), position(C, FOUR), DOWN_LEFT) + ); + } + + @DisplayName("색상별 방향을 계산할 수 없는 춮발지 & 목적지가 입력되면 예외를 발생시킨다.") + @MethodSource("throwExceptionWhenInputInvalidPositionTestCase") + @ParameterizedTest + void throwExceptionWhenInputInvalidPositionTest(final PieceColor pieceColor, final Position source, final Position destination) { + // When & Then + assertThatThrownBy(() -> PawnMovementDirection.calculateDirection(pieceColor, source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(pieceColor.name() + "색상의 폰이 이동할 수 없는 방향입니다."); + } + + private static Stream throwExceptionWhenInputInvalidPositionTestCase() { + return Stream.of( + Arguments.of(BLACK, position(C, TWO), position(C, FOUR)), + Arguments.of(BLACK, position(C, FOUR), position(C, FIVE)), + Arguments.of(BLACK, position(D, FIVE), position(E, SIX)), + Arguments.of(BLACK, position(D, FIVE), position(C, SIX)), + Arguments.of(WHITE, position(C, SEVEN), position(C, FIVE)), + Arguments.of(WHITE, position(C, FOUR), position(C, THREE)), + Arguments.of(WHITE, position(D, FIVE), position(E, FOUR)), + Arguments.of(WHITE, position(D, FIVE), position(C, FOUR)), + Arguments.of(WHITE, position(B, TWO), position(B, SIX)), + Arguments.of(WHITE, position(B, TWO), position(B, ONE)), + Arguments.of(WHITE, position(B, TWO), position(A, TWO)), + Arguments.of(WHITE, position(B, TWO), position(C, ONE)), + Arguments.of(BLACK, position(B, TWO), position(D, TWO)), + Arguments.of(BLACK, position(B, TWO), position(E, FIVE)), + Arguments.of(BLACK, position(B, TWO), position(A, THREE)) + ); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From 0de34b72f10907eaf3a2115429f5ca7b58d5905f Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 00:42:06 +0900 Subject: [PATCH 41/54] =?UTF-8?q?refactor:=20board=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Board.java | 2 +- src/main/java/domain/BoardInitializer.java | 8 ++++---- src/main/java/domain/Piece.java | 2 +- src/main/java/domain/PieceFactory.java | 2 +- src/main/java/domain/{position => board}/File.java | 2 +- .../java/domain/{position => board}/Position.java | 4 +++- src/main/java/domain/{position => board}/Rank.java | 2 +- src/main/java/domain/piece/Bishop.java | 5 ++--- .../{position => piece}/CommonMovementDirection.java | 4 +++- src/main/java/domain/piece/King.java | 5 ++--- src/main/java/domain/piece/Knight.java | 5 ++--- .../{position => piece}/KnightMovementDirection.java | 4 +++- .../{position => piece}/MovementDirection.java | 2 +- src/main/java/domain/piece/Pawn.java | 9 ++++----- .../{position => piece}/PawnMovementDirection.java | 4 ++-- src/main/java/domain/piece/Piece.java | 2 +- src/main/java/domain/piece/Queen.java | 5 ++--- src/main/java/domain/piece/Rook.java | 5 ++--- .../java/domain/strategy/ContinuousMoveStrategy.java | 4 ++-- .../java/domain/strategy/KnightMoveStrategy.java | 4 ++-- src/main/java/domain/strategy/MoveStrategy.java | 2 +- src/main/java/domain/strategy/PawnMoveStrategy.java | 4 ++-- src/main/java/dto/BoardDto.java | 2 +- src/main/java/dto/MovePositionDto.java | 2 +- src/main/java/dto/RequestDto.java | 2 +- src/main/java/view/OutputView.java | 2 +- src/main/java/view/PositionConvertor.java | 10 +++++----- src/test/java/domain/BoardTest.java | 6 +++--- src/test/java/domain/piece/BishopTest.java | 6 +++--- src/test/java/domain/piece/KingTest.java | 6 +++--- src/test/java/domain/piece/KnightTest.java | 6 +++--- src/test/java/domain/piece/PawnTest.java | 12 ++++++------ src/test/java/domain/piece/QueenTest.java | 6 +++--- src/test/java/domain/piece/RookTest.java | 6 +++--- .../domain/position/CommonMovementDirectionTest.java | 10 +++++++--- src/test/java/domain/position/FileTest.java | 1 + .../domain/position/KnightMovementDirectionTest.java | 10 +++++++--- .../domain/position/PawnMovementDirectionTest.java | 10 +++++++--- src/test/java/domain/position/PositionTest.java | 4 ++++ src/test/java/domain/position/RankTest.java | 1 + .../domain/strategy/ContinuousMoveStrategyTest.java | 10 +++++----- .../java/domain/strategy/KnightMoveStrategyTest.java | 6 +++--- .../java/domain/strategy/PawnMoveStrategyTest.java | 6 +++--- 43 files changed, 114 insertions(+), 96 deletions(-) rename src/main/java/domain/{position => board}/File.java (95%) rename src/main/java/domain/{position => board}/Position.java (88%) rename src/main/java/domain/{position => board}/Rank.java (95%) rename src/main/java/domain/{position => piece}/CommonMovementDirection.java (98%) rename src/main/java/domain/{position => piece}/KnightMovementDirection.java (97%) rename src/main/java/domain/{position => piece}/MovementDirection.java (79%) rename src/main/java/domain/{position => piece}/PawnMovementDirection.java (97%) diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java index 201a8fff0fd..74d2426da32 100644 --- a/src/main/java/domain/Board.java +++ b/src/main/java/domain/Board.java @@ -1,6 +1,6 @@ package domain; -import domain.position.Position; +import domain.board.Position; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/domain/BoardInitializer.java b/src/main/java/domain/BoardInitializer.java index 1a539a9d7d3..9be553c0023 100644 --- a/src/main/java/domain/BoardInitializer.java +++ b/src/main/java/domain/BoardInitializer.java @@ -1,7 +1,7 @@ package domain; -import domain.position.File; -import domain.position.Position; +import domain.board.File; +import domain.board.Position; import java.util.Arrays; import java.util.HashMap; @@ -9,8 +9,8 @@ import java.util.Map; import static domain.PieceType.*; -import static domain.position.File.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.board.Rank.*; public class BoardInitializer { private static final Map> pieceInitialPositions = Map.ofEntries( diff --git a/src/main/java/domain/Piece.java b/src/main/java/domain/Piece.java index aab5f66be86..c7757ca5860 100644 --- a/src/main/java/domain/Piece.java +++ b/src/main/java/domain/Piece.java @@ -1,6 +1,6 @@ package domain; -import domain.position.Position; +import domain.board.Position; import domain.strategy.MoveStrategy; import java.util.Set; diff --git a/src/main/java/domain/PieceFactory.java b/src/main/java/domain/PieceFactory.java index 978b395e525..122004ad64e 100644 --- a/src/main/java/domain/PieceFactory.java +++ b/src/main/java/domain/PieceFactory.java @@ -1,6 +1,6 @@ package domain; -import domain.position.CommonMovementDirection; +import domain.piece.CommonMovementDirection; import domain.strategy.ContinuousMoveStrategy; import domain.strategy.KnightMoveStrategy; import domain.strategy.MoveStrategy; diff --git a/src/main/java/domain/position/File.java b/src/main/java/domain/board/File.java similarity index 95% rename from src/main/java/domain/position/File.java rename to src/main/java/domain/board/File.java index dd371f131cb..146a1102098 100644 --- a/src/main/java/domain/position/File.java +++ b/src/main/java/domain/board/File.java @@ -1,4 +1,4 @@ -package domain.position; +package domain.board; import java.util.Arrays; diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/board/Position.java similarity index 88% rename from src/main/java/domain/position/Position.java rename to src/main/java/domain/board/Position.java index 06ed49e14f9..6536da81cbf 100644 --- a/src/main/java/domain/position/Position.java +++ b/src/main/java/domain/board/Position.java @@ -1,4 +1,6 @@ -package domain.position; +package domain.board; + +import domain.piece.MovementDirection; public record Position(File file, Rank rank) { diff --git a/src/main/java/domain/position/Rank.java b/src/main/java/domain/board/Rank.java similarity index 95% rename from src/main/java/domain/position/Rank.java rename to src/main/java/domain/board/Rank.java index 063ca9fd959..fb82e012bc4 100644 --- a/src/main/java/domain/position/Rank.java +++ b/src/main/java/domain/board/Rank.java @@ -1,4 +1,4 @@ -package domain.position; +package domain.board; import java.util.Arrays; diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index d55731b6a20..bdd41f8d708 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -1,13 +1,12 @@ package domain.piece; -import domain.position.CommonMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.position.CommonMovementDirection.*; +import static domain.piece.CommonMovementDirection.*; public class Bishop extends Piece { private static final List MOVABLE_DIRECTIONS = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); diff --git a/src/main/java/domain/position/CommonMovementDirection.java b/src/main/java/domain/piece/CommonMovementDirection.java similarity index 98% rename from src/main/java/domain/position/CommonMovementDirection.java rename to src/main/java/domain/piece/CommonMovementDirection.java index 928b628da12..ac9791d4faa 100644 --- a/src/main/java/domain/position/CommonMovementDirection.java +++ b/src/main/java/domain/piece/CommonMovementDirection.java @@ -1,4 +1,6 @@ -package domain.position; +package domain.piece; + +import domain.board.Position; import java.util.Arrays; import java.util.Set; diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java index 28d4c3ebb32..862aafc831b 100644 --- a/src/main/java/domain/piece/King.java +++ b/src/main/java/domain/piece/King.java @@ -1,11 +1,10 @@ package domain.piece; -import domain.position.CommonMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.Map; -import static domain.position.CommonMovementDirection.calculateDirection; +import static domain.piece.CommonMovementDirection.calculateDirection; public class King extends Piece { diff --git a/src/main/java/domain/piece/Knight.java b/src/main/java/domain/piece/Knight.java index 690b5d6a55c..37a10df8d99 100644 --- a/src/main/java/domain/piece/Knight.java +++ b/src/main/java/domain/piece/Knight.java @@ -1,11 +1,10 @@ package domain.piece; -import domain.position.KnightMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.Map; -import static domain.position.KnightMovementDirection.calculateDirection; +import static domain.piece.KnightMovementDirection.calculateDirection; public class Knight extends Piece { diff --git a/src/main/java/domain/position/KnightMovementDirection.java b/src/main/java/domain/piece/KnightMovementDirection.java similarity index 97% rename from src/main/java/domain/position/KnightMovementDirection.java rename to src/main/java/domain/piece/KnightMovementDirection.java index e021b3e2bbc..eda0dd7d451 100644 --- a/src/main/java/domain/position/KnightMovementDirection.java +++ b/src/main/java/domain/piece/KnightMovementDirection.java @@ -1,4 +1,6 @@ -package domain.position; +package domain.piece; + +import domain.board.Position; import java.util.Arrays; diff --git a/src/main/java/domain/position/MovementDirection.java b/src/main/java/domain/piece/MovementDirection.java similarity index 79% rename from src/main/java/domain/position/MovementDirection.java rename to src/main/java/domain/piece/MovementDirection.java index b9626377967..e0413387989 100644 --- a/src/main/java/domain/position/MovementDirection.java +++ b/src/main/java/domain/piece/MovementDirection.java @@ -1,4 +1,4 @@ -package domain.position; +package domain.piece; public interface MovementDirection { diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java index 24ed167fd43..f02041a44f2 100644 --- a/src/main/java/domain/piece/Pawn.java +++ b/src/main/java/domain/piece/Pawn.java @@ -1,15 +1,14 @@ package domain.piece; -import domain.position.PawnMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.Map; import static domain.piece.PieceColor.BLACK; import static domain.piece.PieceColor.WHITE; -import static domain.position.PawnMovementDirection.calculateDirection; -import static domain.position.Rank.SEVEN; -import static domain.position.Rank.TWO; +import static domain.piece.PawnMovementDirection.calculateDirection; +import static domain.board.Rank.SEVEN; +import static domain.board.Rank.TWO; public class Pawn extends Piece { diff --git a/src/main/java/domain/position/PawnMovementDirection.java b/src/main/java/domain/piece/PawnMovementDirection.java similarity index 97% rename from src/main/java/domain/position/PawnMovementDirection.java rename to src/main/java/domain/piece/PawnMovementDirection.java index 54d349ca276..ac3fb41e73b 100644 --- a/src/main/java/domain/position/PawnMovementDirection.java +++ b/src/main/java/domain/piece/PawnMovementDirection.java @@ -1,6 +1,6 @@ -package domain.position; +package domain.piece; -import domain.piece.PieceColor; +import domain.board.Position; import java.util.Arrays; diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index 71f086bc88d..c916dd29b91 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -1,6 +1,6 @@ package domain.piece; -import domain.position.Position; +import domain.board.Position; import java.util.Map; diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index fe6d3707e27..545a5556ce8 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -1,13 +1,12 @@ package domain.piece; -import domain.position.CommonMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.position.CommonMovementDirection.*; +import static domain.piece.CommonMovementDirection.*; public class Queen extends Piece { diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index 66c7610d1d7..a10e3e73854 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -1,13 +1,12 @@ package domain.piece; -import domain.position.CommonMovementDirection; -import domain.position.Position; +import domain.board.Position; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.position.CommonMovementDirection.*; +import static domain.piece.CommonMovementDirection.*; public class Rook extends Piece { private static final List MOVABLE_DIRECTIONS = List.of(UP, DOWN, RIGHT, LEFT); diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java index 0f54c107965..5100963f78f 100644 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ b/src/main/java/domain/strategy/ContinuousMoveStrategy.java @@ -1,7 +1,7 @@ package domain.strategy; -import domain.position.Position; -import domain.position.CommonMovementDirection; +import domain.board.Position; +import domain.piece.CommonMovementDirection; import java.util.List; import java.util.Set; diff --git a/src/main/java/domain/strategy/KnightMoveStrategy.java b/src/main/java/domain/strategy/KnightMoveStrategy.java index 26f546ce981..466adaf9539 100644 --- a/src/main/java/domain/strategy/KnightMoveStrategy.java +++ b/src/main/java/domain/strategy/KnightMoveStrategy.java @@ -1,7 +1,7 @@ package domain.strategy; -import domain.position.KnightMovementDirection; -import domain.position.Position; +import domain.piece.KnightMovementDirection; +import domain.board.Position; import java.util.Set; diff --git a/src/main/java/domain/strategy/MoveStrategy.java b/src/main/java/domain/strategy/MoveStrategy.java index 104c9ff2578..23e0d5fbd8d 100644 --- a/src/main/java/domain/strategy/MoveStrategy.java +++ b/src/main/java/domain/strategy/MoveStrategy.java @@ -1,6 +1,6 @@ package domain.strategy; -import domain.position.Position; +import domain.board.Position; import java.util.Set; diff --git a/src/main/java/domain/strategy/PawnMoveStrategy.java b/src/main/java/domain/strategy/PawnMoveStrategy.java index 6f955935ae3..21937be2de4 100644 --- a/src/main/java/domain/strategy/PawnMoveStrategy.java +++ b/src/main/java/domain/strategy/PawnMoveStrategy.java @@ -1,8 +1,8 @@ package domain.strategy; import domain.TeamColor; -import domain.position.Position; -import domain.position.Rank; +import domain.board.Position; +import domain.board.Rank; import java.util.Set; diff --git a/src/main/java/dto/BoardDto.java b/src/main/java/dto/BoardDto.java index f9c01aac752..bcde484b0ec 100644 --- a/src/main/java/dto/BoardDto.java +++ b/src/main/java/dto/BoardDto.java @@ -2,7 +2,7 @@ import domain.Board; import domain.PieceType; -import domain.position.Position; +import domain.board.Position; import java.util.Collections; import java.util.Map; diff --git a/src/main/java/dto/MovePositionDto.java b/src/main/java/dto/MovePositionDto.java index a914b9ef7d7..46f6f64d71f 100644 --- a/src/main/java/dto/MovePositionDto.java +++ b/src/main/java/dto/MovePositionDto.java @@ -1,6 +1,6 @@ package dto; -import domain.position.Position; +import domain.board.Position; import java.util.Collections; import java.util.List; diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java index 7de58fc67c9..68aa98c54d2 100644 --- a/src/main/java/dto/RequestDto.java +++ b/src/main/java/dto/RequestDto.java @@ -1,7 +1,7 @@ package dto; import domain.GameCommand; -import domain.position.Position; +import domain.board.Position; public record RequestDto(GameCommand command, MovePositionDto movePositionDto) { diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 82f25fe0aef..1128b2e0c5b 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,7 +1,7 @@ package view; import domain.PieceType; -import domain.position.Position; +import domain.board.Position; import dto.BoardDto; import java.util.Arrays; diff --git a/src/main/java/view/PositionConvertor.java b/src/main/java/view/PositionConvertor.java index 6b4950e245d..c9ba2f2e484 100644 --- a/src/main/java/view/PositionConvertor.java +++ b/src/main/java/view/PositionConvertor.java @@ -1,13 +1,13 @@ package view; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import java.util.Map; -import static domain.position.File.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.board.Rank.*; public class PositionConvertor { private static final Map files = Map.of( diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java index 79226935db6..56028dccfc5 100644 --- a/src/test/java/domain/BoardTest.java +++ b/src/test/java/domain/BoardTest.java @@ -1,8 +1,8 @@ package domain; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/piece/BishopTest.java b/src/test/java/domain/piece/BishopTest.java index 2c05fdfd27f..84a7514c12c 100644 --- a/src/test/java/domain/piece/BishopTest.java +++ b/src/test/java/domain/piece/BishopTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/domain/piece/KingTest.java b/src/test/java/domain/piece/KingTest.java index ee0bc874404..59993719e37 100644 --- a/src/test/java/domain/piece/KingTest.java +++ b/src/test/java/domain/piece/KingTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/domain/piece/KnightTest.java b/src/test/java/domain/piece/KnightTest.java index ba63ffb1717..539243b516b 100644 --- a/src/test/java/domain/piece/KnightTest.java +++ b/src/test/java/domain/piece/KnightTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/piece/PawnTest.java b/src/test/java/domain/piece/PawnTest.java index 65bdde31bbe..08f12a18550 100644 --- a/src/test/java/domain/piece/PawnTest.java +++ b/src/test/java/domain/piece/PawnTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -14,9 +14,9 @@ import static domain.piece.PieceColor.BLACK; import static domain.piece.PieceColor.WHITE; -import static domain.position.File.*; -import static domain.position.PawnMovementDirection.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.piece.PawnMovementDirection.*; +import static domain.board.Rank.*; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/domain/piece/QueenTest.java b/src/test/java/domain/piece/QueenTest.java index 78ec2b1dec2..f2174f58ade 100644 --- a/src/test/java/domain/piece/QueenTest.java +++ b/src/test/java/domain/piece/QueenTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/domain/piece/RookTest.java b/src/test/java/domain/piece/RookTest.java index 2889cf6b175..9ae4e0db1e2 100644 --- a/src/test/java/domain/piece/RookTest.java +++ b/src/test/java/domain/piece/RookTest.java @@ -1,8 +1,8 @@ package domain.piece; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/domain/position/CommonMovementDirectionTest.java b/src/test/java/domain/position/CommonMovementDirectionTest.java index 26776a306bd..5b3adb309cd 100644 --- a/src/test/java/domain/position/CommonMovementDirectionTest.java +++ b/src/test/java/domain/position/CommonMovementDirectionTest.java @@ -1,5 +1,9 @@ package domain.position; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; +import domain.piece.CommonMovementDirection; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -7,9 +11,9 @@ import java.util.stream.Stream; -import static domain.position.File.*; -import static domain.position.CommonMovementDirection.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.piece.CommonMovementDirection.*; +import static domain.board.Rank.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/domain/position/FileTest.java b/src/test/java/domain/position/FileTest.java index 8a88ee77e0e..56b8f21370f 100644 --- a/src/test/java/domain/position/FileTest.java +++ b/src/test/java/domain/position/FileTest.java @@ -1,5 +1,6 @@ package domain.position; +import domain.board.File; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/position/KnightMovementDirectionTest.java b/src/test/java/domain/position/KnightMovementDirectionTest.java index 45bc34333fc..c6586bf5697 100644 --- a/src/test/java/domain/position/KnightMovementDirectionTest.java +++ b/src/test/java/domain/position/KnightMovementDirectionTest.java @@ -1,5 +1,9 @@ package domain.position; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; +import domain.piece.KnightMovementDirection; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -7,9 +11,9 @@ import java.util.stream.Stream; -import static domain.position.File.*; -import static domain.position.KnightMovementDirection.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.piece.KnightMovementDirection.*; +import static domain.board.Rank.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/domain/position/PawnMovementDirectionTest.java b/src/test/java/domain/position/PawnMovementDirectionTest.java index 02c6e9a6bf6..b9d8893ddcb 100644 --- a/src/test/java/domain/position/PawnMovementDirectionTest.java +++ b/src/test/java/domain/position/PawnMovementDirectionTest.java @@ -1,5 +1,9 @@ package domain.position; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; +import domain.piece.PawnMovementDirection; import domain.piece.PieceColor; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; @@ -9,9 +13,9 @@ import java.util.stream.Stream; import static domain.piece.PieceColor.*; -import static domain.position.File.*; -import static domain.position.PawnMovementDirection.*; -import static domain.position.Rank.*; +import static domain.board.File.*; +import static domain.piece.PawnMovementDirection.*; +import static domain.board.Rank.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/domain/position/PositionTest.java b/src/test/java/domain/position/PositionTest.java index 64ca031bbc9..121870d3032 100644 --- a/src/test/java/domain/position/PositionTest.java +++ b/src/test/java/domain/position/PositionTest.java @@ -1,5 +1,9 @@ package domain.position; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; +import domain.piece.CommonMovementDirection; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/position/RankTest.java b/src/test/java/domain/position/RankTest.java index 948b3f0311f..6411e9401c9 100644 --- a/src/test/java/domain/position/RankTest.java +++ b/src/test/java/domain/position/RankTest.java @@ -1,5 +1,6 @@ package domain.position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java index 3f3632b84b4..f9bdbd42d09 100644 --- a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java +++ b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java @@ -1,7 +1,7 @@ package domain.strategy; -import domain.position.Position; -import domain.position.CommonMovementDirection; +import domain.board.Position; +import domain.piece.CommonMovementDirection; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -11,9 +11,9 @@ import java.util.Set; import java.util.stream.Stream; -import static domain.position.File.*; -import static domain.position.Rank.*; -import static domain.position.CommonMovementDirection.*; +import static domain.board.File.*; +import static domain.board.Rank.*; +import static domain.piece.CommonMovementDirection.*; import static org.assertj.core.api.Assertions.assertThat; class ContinuousMoveStrategyTest { diff --git a/src/test/java/domain/strategy/KnightMoveStrategyTest.java b/src/test/java/domain/strategy/KnightMoveStrategyTest.java index 944e391d6db..25915134c48 100644 --- a/src/test/java/domain/strategy/KnightMoveStrategyTest.java +++ b/src/test/java/domain/strategy/KnightMoveStrategyTest.java @@ -1,8 +1,8 @@ package domain.strategy; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/domain/strategy/PawnMoveStrategyTest.java b/src/test/java/domain/strategy/PawnMoveStrategyTest.java index 46007cdd156..7f7f092a5a5 100644 --- a/src/test/java/domain/strategy/PawnMoveStrategyTest.java +++ b/src/test/java/domain/strategy/PawnMoveStrategyTest.java @@ -1,9 +1,9 @@ package domain.strategy; import domain.TeamColor; -import domain.position.File; -import domain.position.Position; -import domain.position.Rank; +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; From 332efb53ab5fbbb6fe5aa5c46a3e17dbc1ca4d0e Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 03:00:53 +0900 Subject: [PATCH 42/54] =?UTF-8?q?refactor:=20Piece=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20Type=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyu성nguk Ryu --- src/main/java/domain/piece/Bishop.java | 8 ++++++++ src/main/java/domain/piece/King.java | 8 ++++++++ src/main/java/domain/piece/Knight.java | 8 ++++++++ src/main/java/domain/piece/Pawn.java | 14 +++++++++++--- src/main/java/domain/piece/Piece.java | 9 ++++++++- src/main/java/domain/piece/Queen.java | 8 ++++++++ src/main/java/domain/piece/Rook.java | 8 ++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index bdd41f8d708..a9a29ef127f 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -1,15 +1,18 @@ package domain.piece; import domain.board.Position; +import domain.game.PieceType; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static domain.game.PieceType.BISHOP; import static domain.piece.CommonMovementDirection.*; public class Bishop extends Piece { private static final List MOVABLE_DIRECTIONS = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); + private static final PieceType PIECE_TYPE = BISHOP; public Bishop(final PieceColor color) { super(color); @@ -62,4 +65,9 @@ private void checkAlivePosition(final Position alivePosition, final Map p throw new IllegalArgumentException("기물이 존재하는 칸으로 이동할 수 없습니다."); } } + + @Override + public PieceType pieceType() { + return PIECE_TYPE; + } } diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index c916dd29b91..c0769f2c903 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -1,18 +1,21 @@ package domain.piece; import domain.board.Position; +import domain.game.PieceType; import java.util.Map; public abstract class Piece { protected final PieceColor color; - protected Piece(final PieceColor color) { + public Piece(final PieceColor color) { this.color = color; } public abstract void checkMovable(final Position source, final Position destination, final Map piecePositions); + public abstract PieceType pieceType(); + protected boolean checkEnemy(final Piece otherPiece) { return otherPiece.isEnemy(this.color); } @@ -20,4 +23,8 @@ protected boolean checkEnemy(final Piece otherPiece) { public boolean isEnemy(final PieceColor otherColor) { return this.color != otherColor; } + + public PieceColor pieceColor() { + return color; + } } diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index 545a5556ce8..c343bdce5ac 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -1,14 +1,17 @@ package domain.piece; import domain.board.Position; +import domain.game.PieceType; import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static domain.game.PieceType.QUEEN; import static domain.piece.CommonMovementDirection.*; public class Queen extends Piece { + private static final PieceType PIECE_TYPE = QUEEN; public Queen(final PieceColor color) { super(color); @@ -54,4 +57,9 @@ private void checkAlivePosition(final Position alivePosition, final Map MOVABLE_DIRECTIONS = List.of(UP, DOWN, RIGHT, LEFT); + private static final PieceType PIECE_TYPE = ROOK; public Rook(final PieceColor color) { super(color); @@ -62,4 +65,9 @@ private void checkAlivePosition(final Position alivePosition, final Map Date: Sun, 24 Mar 2024 03:03:11 +0900 Subject: [PATCH 43/54] =?UTF-8?q?remove:=20=EA=B8=B0=EC=A1=B4=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyunguk Ryu <가hw0603@naver.com> --- src/main/java/Application.java | 13 -- src/main/java/controller/ChessController.java | 61 -------- src/main/java/domain/Board.java | 55 ------- src/main/java/domain/BoardInitializer.java | 39 ----- src/main/java/domain/GameCommand.java | 13 -- src/main/java/domain/Piece.java | 28 ---- src/main/java/domain/PieceFactory.java | 42 ------ src/main/java/domain/PieceType.java | 29 ---- src/main/java/domain/TeamColor.java | 12 -- src/main/java/domain/Turn.java | 17 --- .../strategy/ContinuousMoveStrategy.java | 52 ------- .../domain/strategy/KnightMoveStrategy.java | 13 -- .../java/domain/strategy/MoveStrategy.java | 10 -- .../domain/strategy/PawnMoveStrategy.java | 76 ---------- src/main/java/dto/BoardDto.java | 21 --- src/main/java/dto/MovePositionDto.java | 29 ---- src/main/java/dto/RequestDto.java | 23 --- src/main/java/view/InputView.java | 45 ------ src/main/java/view/OutputView.java | 52 ------- src/test/java/domain/BoardTest.java | 87 ----------- .../strategy/ContinuousMoveStrategyTest.java | 137 ------------------ .../strategy/KnightMoveStrategyTest.java | 47 ------ .../domain/strategy/PawnMoveStrategyTest.java | 91 ------------ 23 files changed, 992 deletions(-) delete mode 100644 src/main/java/Application.java delete mode 100644 src/main/java/controller/ChessController.java delete mode 100644 src/main/java/domain/Board.java delete mode 100644 src/main/java/domain/BoardInitializer.java delete mode 100644 src/main/java/domain/GameCommand.java delete mode 100644 src/main/java/domain/Piece.java delete mode 100644 src/main/java/domain/PieceFactory.java delete mode 100644 src/main/java/domain/PieceType.java delete mode 100644 src/main/java/domain/TeamColor.java delete mode 100644 src/main/java/domain/Turn.java delete mode 100644 src/main/java/domain/strategy/ContinuousMoveStrategy.java delete mode 100644 src/main/java/domain/strategy/KnightMoveStrategy.java delete mode 100644 src/main/java/domain/strategy/MoveStrategy.java delete mode 100644 src/main/java/domain/strategy/PawnMoveStrategy.java delete mode 100644 src/main/java/dto/BoardDto.java delete mode 100644 src/main/java/dto/MovePositionDto.java delete mode 100644 src/main/java/dto/RequestDto.java delete mode 100644 src/main/java/view/InputView.java delete mode 100644 src/main/java/view/OutputView.java delete mode 100644 src/test/java/domain/BoardTest.java delete mode 100644 src/test/java/domain/strategy/ContinuousMoveStrategyTest.java delete mode 100644 src/test/java/domain/strategy/KnightMoveStrategyTest.java delete mode 100644 src/test/java/domain/strategy/PawnMoveStrategyTest.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java deleted file mode 100644 index a852778d7e2..00000000000 --- a/src/main/java/Application.java +++ /dev/null @@ -1,13 +0,0 @@ -import controller.ChessController; -import view.InputView; -import view.OutputView; - -public class Application { - public static void main(String[] args) { - InputView inputView = new InputView(); - OutputView outputView = new OutputView(); - ChessController chessController = new ChessController(inputView, outputView); - - chessController.run(); - } -} diff --git a/src/main/java/controller/ChessController.java b/src/main/java/controller/ChessController.java deleted file mode 100644 index 1334a477834..00000000000 --- a/src/main/java/controller/ChessController.java +++ /dev/null @@ -1,61 +0,0 @@ -package controller; - -//import domain.*; - -import domain.*; -import dto.BoardDto; -import dto.RequestDto; -import view.InputView; -import view.OutputView; - -public class ChessController { - private final InputView inputView; - private final OutputView outputView; - - public ChessController(final InputView inputView, final OutputView outputView) { - this.inputView = inputView; - this.outputView = outputView; - } - - public void run() { - outputView.printWelcomeMessage(); - GameCommand command = inputView.inputGameStart(); - if (command.isStart()) { - startGame(); - } - } - - private void startGame() { - Board board = BoardInitializer.init(); - printStatus(board); - - Turn turn = new Turn(); - RequestDto requestDto = inputView.inputGameCommand(); - while (requestDto.command().isContinuable()) { - doTurn(board, turn, requestDto); - printStatus(board); - requestDto = inputView.inputGameCommand(); - } - } - - private void doTurn(final Board board, Turn turn, final RequestDto requestDto) { - try { - board.movePiece(turn.current(), requestDto.source(), requestDto.destination()); - turn.next(); - } catch (IllegalArgumentException e) { - System.out.println("[오류] " + e.getMessage()); - } - } - - private TeamColor defineTeam(boolean isWhiteTurn) { - if (isWhiteTurn) { - return TeamColor.WHITE; - } - return TeamColor.BLACK; - } - - private void printStatus(Board board) { - BoardDto boardDto = BoardDto.from(board); - outputView.printBoard(boardDto); - } -} diff --git a/src/main/java/domain/Board.java b/src/main/java/domain/Board.java deleted file mode 100644 index 74d2426da32..00000000000 --- a/src/main/java/domain/Board.java +++ /dev/null @@ -1,55 +0,0 @@ -package domain; - -import domain.board.Position; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class Board { - private final Map chessBoard; - - public Board(final Map chessBoard) { - this.chessBoard = chessBoard; - } - - public Map getChessBoard() { - return Collections.unmodifiableMap(chessBoard); - } - - public void movePiece(final TeamColor teamColor, final Position source, final Position destination) { - validateRequest(teamColor, source); - - Piece piece = chessBoard.get(source); - if (!piece.isMovable(source, destination, parseOtherPiecePositions(source))) { - throw new IllegalArgumentException("이동 위치까지 이동할 수 없습니다."); - } - - // 이동 위치에 아군 기물이 존재 - if (isPieceExist(destination)) { - if (chessBoard.get(destination).hasColor(teamColor)) { - throw new IllegalArgumentException("이동 위치에 아군 기물이 존재합니다."); - } - } - // 공격 없이 빈 칸으로 이동하는 경우 - chessBoard.put(destination, piece); - chessBoard.remove(source); - } - - private Set parseOtherPiecePositions(final Position source) { - Set positions = new HashSet<>(chessBoard.keySet()); - positions.remove(source); - return positions; - } - - private void validateRequest(final TeamColor teamColor, final Position source) { - if (!chessBoard.containsKey(source) || !chessBoard.get(source).hasColor(teamColor)) { - throw new IllegalArgumentException("차례가 맞지 않습니다."); - } - } - - private boolean isPieceExist(Position position) { - return chessBoard.containsKey(position); - } -} diff --git a/src/main/java/domain/BoardInitializer.java b/src/main/java/domain/BoardInitializer.java deleted file mode 100644 index 9be553c0023..00000000000 --- a/src/main/java/domain/BoardInitializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package domain; - -import domain.board.File; -import domain.board.Position; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static domain.PieceType.*; -import static domain.board.File.*; -import static domain.board.Rank.*; - -public class BoardInitializer { - private static final Map> pieceInitialPositions = Map.ofEntries( - Map.entry(BLACK_PAWN, Arrays.stream(File.values()).map(file -> new Position(file, SEVEN)).toList()), - Map.entry(BLACK_ROOK, List.of(new Position(A, EIGHT), new Position(H, EIGHT))), - Map.entry(BLACK_KNIGHT, List.of(new Position(B, EIGHT), new Position(G, EIGHT))), - Map.entry(BLACK_BISHOP, List.of(new Position(C, EIGHT), new Position(F, EIGHT))), - Map.entry(BLACK_QUEEN, List.of(new Position(D, EIGHT))), - Map.entry(BLACK_KING, List.of(new Position(E, EIGHT))), - Map.entry(WHITE_PAWN, Arrays.stream(File.values()).map(file -> new Position(file, TWO)).toList()), - Map.entry(WHITE_ROOK, List.of(new Position(A, ONE), new Position(H, ONE))), - Map.entry(WHITE_KNIGHT, List.of(new Position(B, ONE), new Position(G, ONE))), - Map.entry(WHITE_BISHOP, List.of(new Position(C, ONE), new Position(F, ONE))), - Map.entry(WHITE_QUEEN, List.of(new Position(D, ONE))), - Map.entry(WHITE_KING, List.of(new Position(E, ONE))) - ); - - public static Board init() { - Map positions = new HashMap<>(); - pieceInitialPositions.forEach(((pieceType, position) -> { - position.forEach(p -> positions.put(p, PieceFactory.create(pieceType))); - })); - - return new Board(positions); - } -} diff --git a/src/main/java/domain/GameCommand.java b/src/main/java/domain/GameCommand.java deleted file mode 100644 index 3d38ad62863..00000000000 --- a/src/main/java/domain/GameCommand.java +++ /dev/null @@ -1,13 +0,0 @@ -package domain; - -public enum GameCommand { - START, MOVE, END; - - public boolean isContinuable() { - return this != END; - } - - public boolean isStart() { - return this == START; - } -} diff --git a/src/main/java/domain/Piece.java b/src/main/java/domain/Piece.java deleted file mode 100644 index c7757ca5860..00000000000 --- a/src/main/java/domain/Piece.java +++ /dev/null @@ -1,28 +0,0 @@ -package domain; - -import domain.board.Position; -import domain.strategy.MoveStrategy; - -import java.util.Set; - -public class Piece { - private final PieceType pieceType; - private final MoveStrategy moveStrategy; - - public Piece(final PieceType pieceType, final MoveStrategy moveStrategy) { - this.pieceType = pieceType; - this.moveStrategy = moveStrategy; - } - - public PieceType getPieceType() { - return pieceType; - } - - public boolean hasColor(final TeamColor teamColor) { - return pieceType.hasColor(teamColor); - } - - public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - return moveStrategy.isMovable(source, destination, piecePositions); - } -} diff --git a/src/main/java/domain/PieceFactory.java b/src/main/java/domain/PieceFactory.java deleted file mode 100644 index 122004ad64e..00000000000 --- a/src/main/java/domain/PieceFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -package domain; - -import domain.piece.CommonMovementDirection; -import domain.strategy.ContinuousMoveStrategy; -import domain.strategy.KnightMoveStrategy; -import domain.strategy.MoveStrategy; -import domain.strategy.PawnMoveStrategy; - -import java.util.Map; - -import static domain.PieceType.*; - -public class PieceFactory { - private static final int MAXIMUM_MOVE_BOUND = 8; - private static final int KING_MAXIMUM_MOVE_BOUND = 1; - private static final MoveStrategy whitePawnStrategy = new PawnMoveStrategy(TeamColor.WHITE); - private static final MoveStrategy blackPawnStrategy = new PawnMoveStrategy(TeamColor.BLACK); - private static final MoveStrategy knightStrategy = new KnightMoveStrategy(); - private static final MoveStrategy rookStrategy = new ContinuousMoveStrategy(CommonMovementDirection.orthogonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy bishopStrategy = new ContinuousMoveStrategy(CommonMovementDirection.diagonalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy queenStrategy = new ContinuousMoveStrategy(CommonMovementDirection.omnidirectionalVectors(), MAXIMUM_MOVE_BOUND); - private static final MoveStrategy kingStrategy = new ContinuousMoveStrategy(CommonMovementDirection.omnidirectionalVectors(), KING_MAXIMUM_MOVE_BOUND); - - private static final Map moveStrategies = Map.ofEntries( - Map.entry(BLACK_PAWN, blackPawnStrategy), - Map.entry(BLACK_ROOK, rookStrategy), - Map.entry(BLACK_KNIGHT, knightStrategy), - Map.entry(BLACK_BISHOP, bishopStrategy), - Map.entry(BLACK_QUEEN, queenStrategy), - Map.entry(BLACK_KING, kingStrategy), - Map.entry(WHITE_PAWN, whitePawnStrategy), - Map.entry(WHITE_ROOK, rookStrategy), - Map.entry(WHITE_KNIGHT, knightStrategy), - Map.entry(WHITE_BISHOP, bishopStrategy), - Map.entry(WHITE_QUEEN, queenStrategy), - Map.entry(WHITE_KING, kingStrategy) - ); - - public static Piece create(PieceType pieceType) { - return new Piece(pieceType, moveStrategies.get(pieceType)); - } -} diff --git a/src/main/java/domain/PieceType.java b/src/main/java/domain/PieceType.java deleted file mode 100644 index b8a7ea74aab..00000000000 --- a/src/main/java/domain/PieceType.java +++ /dev/null @@ -1,29 +0,0 @@ -package domain; - -import static domain.TeamColor.BLACK; -import static domain.TeamColor.WHITE; - -public enum PieceType { - BLACK_PAWN(BLACK), - BLACK_ROOK(BLACK), - BLACK_KNIGHT(BLACK), - BLACK_BISHOP(BLACK), - BLACK_QUEEN(BLACK), - BLACK_KING(BLACK), - WHITE_PAWN(WHITE), - WHITE_ROOK(WHITE), - WHITE_KNIGHT(WHITE), - WHITE_BISHOP(WHITE), - WHITE_QUEEN(WHITE), - WHITE_KING(WHITE); - - private final TeamColor teamColor; - - PieceType(final TeamColor teamColor) { - this.teamColor = teamColor; - } - - public boolean hasColor(TeamColor teamColor) { - return this.teamColor.equals(teamColor); - } -} diff --git a/src/main/java/domain/TeamColor.java b/src/main/java/domain/TeamColor.java deleted file mode 100644 index 1d92bfaf726..00000000000 --- a/src/main/java/domain/TeamColor.java +++ /dev/null @@ -1,12 +0,0 @@ -package domain; - -public enum TeamColor { - BLACK, WHITE; - - public TeamColor toggle() { - if (this == BLACK) { - return WHITE; - } - return BLACK; - } -} diff --git a/src/main/java/domain/Turn.java b/src/main/java/domain/Turn.java deleted file mode 100644 index d5a082e331f..00000000000 --- a/src/main/java/domain/Turn.java +++ /dev/null @@ -1,17 +0,0 @@ -package domain; - -public class Turn { - TeamColor currentTurn; - - public Turn() { - this.currentTurn = TeamColor.WHITE; - } - - public void next() { - currentTurn = currentTurn.toggle(); - } - - public TeamColor current() { - return currentTurn; - } -} diff --git a/src/main/java/domain/strategy/ContinuousMoveStrategy.java b/src/main/java/domain/strategy/ContinuousMoveStrategy.java deleted file mode 100644 index 5100963f78f..00000000000 --- a/src/main/java/domain/strategy/ContinuousMoveStrategy.java +++ /dev/null @@ -1,52 +0,0 @@ -package domain.strategy; - -import domain.board.Position; -import domain.piece.CommonMovementDirection; - -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -public class ContinuousMoveStrategy implements MoveStrategy { - private final Set acceptableVectors; - private final int moveBound; - - public ContinuousMoveStrategy(Set acceptableVectors, int moveBound) { - this.acceptableVectors = acceptableVectors; - this.moveBound = moveBound; - } - - @Override - public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - CommonMovementDirection optimalVector = findOptimalVector(source, destination); - - if (!acceptableVectors.contains(optimalVector)) { - return false; - } - - List movePaths = Stream.iterate(source, position -> position.next(optimalVector)) - .takeWhile(position -> isContinuable(position, destination, piecePositions)) - .limit(moveBound) - .toList(); - - return isReachable(destination, optimalVector, movePaths); - } - - private static boolean isContinuable(final Position current, final Position destination, final Set piecePositions) { - boolean isReachedDestination = current.equals(destination); - boolean isOtherPieceExist = piecePositions.contains(current); - return !isReachedDestination && !isOtherPieceExist; - } - - private boolean isReachable(final Position destination, final CommonMovementDirection optimalVector, final List movePaths) { - Position finalPosition = movePaths.get(movePaths.size() - 1).next(optimalVector); - return finalPosition.equals(destination); - } - - private CommonMovementDirection findOptimalVector(final Position source, final Position destination) { - int rowDiff = destination.rowIndex() - source.rowIndex(); - int colDiff = destination.columnIndex() - source.columnIndex(); - - return CommonMovementDirection.calculateDirection(source, destination); - } -} diff --git a/src/main/java/domain/strategy/KnightMoveStrategy.java b/src/main/java/domain/strategy/KnightMoveStrategy.java deleted file mode 100644 index 466adaf9539..00000000000 --- a/src/main/java/domain/strategy/KnightMoveStrategy.java +++ /dev/null @@ -1,13 +0,0 @@ -package domain.strategy; - -import domain.piece.KnightMovementDirection; -import domain.board.Position; - -import java.util.Set; - -public class KnightMoveStrategy implements MoveStrategy { - @Override - public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - return KnightMovementDirection.isMovableDirection(source, destination); - } -} diff --git a/src/main/java/domain/strategy/MoveStrategy.java b/src/main/java/domain/strategy/MoveStrategy.java deleted file mode 100644 index 23e0d5fbd8d..00000000000 --- a/src/main/java/domain/strategy/MoveStrategy.java +++ /dev/null @@ -1,10 +0,0 @@ -package domain.strategy; - -import domain.board.Position; - -import java.util.Set; - -@FunctionalInterface -public interface MoveStrategy { - boolean isMovable(Position source, Position destination, Set otherPiecesPosition); -} diff --git a/src/main/java/domain/strategy/PawnMoveStrategy.java b/src/main/java/domain/strategy/PawnMoveStrategy.java deleted file mode 100644 index 21937be2de4..00000000000 --- a/src/main/java/domain/strategy/PawnMoveStrategy.java +++ /dev/null @@ -1,76 +0,0 @@ -package domain.strategy; - -import domain.TeamColor; -import domain.board.Position; -import domain.board.Rank; - -import java.util.Set; - -public class PawnMoveStrategy implements MoveStrategy { - private final TeamColor teamColor; - - public PawnMoveStrategy(final TeamColor teamColor) { - this.teamColor = teamColor; - } - - @Override - public boolean isMovable(final Position source, final Position destination, final Set piecePositions) { - if (teamColor == TeamColor.WHITE) { - return isWhiteMovable(source, destination, piecePositions.contains(destination)); - } - return isBlackMovable(source, destination, piecePositions.contains(destination)); - } - - private boolean isWhiteMovable(final Position source, final Position destination, boolean isExistOtherPiece) { - int rowDiff = destination.rowIndex() - source.rowIndex(); - int colDiff = destination.columnIndex() - source.columnIndex(); - - boolean isStraightMovable = (goUpWhite(rowDiff, colDiff) || goUpUpWhite(rowDiff, colDiff, source)) && !isExistOtherPiece; - boolean isCrossMovable = goCrossWhite(rowDiff, colDiff) && isExistOtherPiece; - - return isStraightMovable || isCrossMovable; - } - - private boolean goUpWhite(int rowDiff, int colDiff) { - return rowDiff == -1 && colDiff == 0; - } - - private boolean goUpUpWhite(int rowDiff, int colDiff, Position source) { - return rowDiff == -2 && colDiff == 0 && isInitialPosition(source); - } - - private boolean goCrossWhite(final int rowDiff, final int colDiff) { - return rowDiff == -1 && Math.abs(colDiff) == 1; - } - - - private boolean isBlackMovable(final Position source, final Position destination, final boolean isExistOtherPiece) { - int rowDiff = destination.rowIndex() - source.rowIndex(); - int colDiff = destination.columnIndex() - source.columnIndex(); - - boolean isStraightMovable = (goUpBlack(rowDiff, colDiff) || goUpUpBlack(rowDiff, colDiff, source)) && !isExistOtherPiece; - boolean isCrossMovable = goCrossBlack(rowDiff, colDiff) && isExistOtherPiece; - - return isStraightMovable || isCrossMovable; - } - - private boolean goUpBlack(int rowDiff, int colDiff) { - return rowDiff == 1 && colDiff == 0; - } - - private boolean goUpUpBlack(int rowDiff, int colDiff, Position source) { - return rowDiff == 2 && colDiff == 0 && isInitialPosition(source); - } - - private boolean goCrossBlack(final int rowDiff, final int colDiff) { - return rowDiff == 1 && Math.abs(colDiff) == 1; - } - - public boolean isInitialPosition(final Position position) { - if (teamColor == TeamColor.BLACK) { - return position.rank() == Rank.SEVEN; - } - - return position.rank() == Rank.TWO; - } -} diff --git a/src/main/java/dto/BoardDto.java b/src/main/java/dto/BoardDto.java deleted file mode 100644 index bcde484b0ec..00000000000 --- a/src/main/java/dto/BoardDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package dto; - -import domain.Board; -import domain.PieceType; -import domain.board.Position; - -import java.util.Collections; -import java.util.Map; -import java.util.stream.Collectors; - -public record BoardDto(Map piecePositions) { - - public static BoardDto from(final Board board) { - Map piecePositions = board.getChessBoard() - .entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getPieceType())); - - return new BoardDto(Collections.unmodifiableMap(piecePositions)); - } -} diff --git a/src/main/java/dto/MovePositionDto.java b/src/main/java/dto/MovePositionDto.java deleted file mode 100644 index 46f6f64d71f..00000000000 --- a/src/main/java/dto/MovePositionDto.java +++ /dev/null @@ -1,29 +0,0 @@ -package dto; - -import domain.board.Position; - -import java.util.Collections; -import java.util.List; - -public record MovePositionDto(List positions) { - - public static MovePositionDto noPosition() { - return new MovePositionDto(Collections.emptyList()); - } - - public static MovePositionDto of(final Position source, final Position destination) { - return new MovePositionDto(List.of(source, destination)); - } - - public boolean hasPosition() { - return !positions.isEmpty(); - } - - public Position source() { - return positions.get(0); - } - - public Position destination() { - return positions.get(1); - } -} diff --git a/src/main/java/dto/RequestDto.java b/src/main/java/dto/RequestDto.java deleted file mode 100644 index 68aa98c54d2..00000000000 --- a/src/main/java/dto/RequestDto.java +++ /dev/null @@ -1,23 +0,0 @@ -package dto; - -import domain.GameCommand; -import domain.board.Position; - -public record RequestDto(GameCommand command, MovePositionDto movePositionDto) { - - public static RequestDto of(GameCommand command) { - return new RequestDto(command, MovePositionDto.noPosition()); - } - - public static RequestDto of(GameCommand command, Position source, Position destination) { - return new RequestDto(command, MovePositionDto.of(source, destination)); - } - - public Position source() { - return movePositionDto.source(); - } - - public Position destination() { - return movePositionDto.destination(); - } -} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java deleted file mode 100644 index ea9ecbb41e1..00000000000 --- a/src/main/java/view/InputView.java +++ /dev/null @@ -1,45 +0,0 @@ -package view; - -import domain.GameCommand; -import dto.RequestDto; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Scanner; - -import static view.PositionConvertor.convertPosition; - -public class InputView { - private static final String START_COMMAND = "start"; - private static final String MOVE_COMMAND = "move"; - private static final String END_COMMAND = "end"; - private static final Map gameCommands = Map.of( - START_COMMAND, GameCommand.START, - MOVE_COMMAND, GameCommand.MOVE, - END_COMMAND, GameCommand.END - ); - - private final Scanner sc = new Scanner(System.in); - - public GameCommand inputGameStart() { - String input = sc.nextLine(); - if (!gameCommands.containsKey(input)) { - throw new IllegalArgumentException("잘못된 명령입니다."); - } - return gameCommands.get(input); - } - - public RequestDto inputGameCommand() { - List input = Arrays.stream(sc.nextLine().split(" ")).toList(); - if (!gameCommands.containsKey(input.get(0))) { - throw new IllegalArgumentException("유효하지 않은 명령입니다."); - } - - if (input.size() == 3) { - return RequestDto.of(gameCommands.get(input.get(0)), convertPosition(input.get(1)), convertPosition(input.get(2))); - } - - return RequestDto.of(gameCommands.get(input.get(0))); - } -} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java deleted file mode 100644 index 1128b2e0c5b..00000000000 --- a/src/main/java/view/OutputView.java +++ /dev/null @@ -1,52 +0,0 @@ -package view; - -import domain.PieceType; -import domain.board.Position; -import dto.BoardDto; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -public class OutputView { - private static final int BOARD_SIZE = 8; - - private final OutputConvertor outputConvertor = new OutputConvertor(); - - public void printWelcomeMessage() { - System.out.println("> 체스 게임을 시작합니다."); - System.out.println("> 게임 시작: start"); - System.out.println("> 게임 종료: end"); - System.out.println("> 게임 이동: move source위치 target위치 - 예. move b2 b3"); - } - - public void printBoard(final BoardDto boardDto) { - List boardStatus = convertBoardStatus(boardDto.piecePositions()); - boardStatus.forEach(System.out::println); - System.out.println(); - } - - private List convertBoardStatus(final Map boardStatus) { - String[][] strings = initStringsArray(); - boardStatus.forEach( - (position, pieceType) -> buildStrings(strings, position, pieceType) - ); - return Arrays.stream(strings) - .map(rowString -> String.join("", rowString)) - .toList(); - } - - private static String[][] initStringsArray() { - String[][] strings = new String[BOARD_SIZE][BOARD_SIZE]; - for (String[] row : strings) { - Arrays.fill(row, "."); - } - return strings; - } - - private void buildStrings(final String[][] strings, final Position position, final PieceType pieceType) { - int row = position.rowIndex(); - int col = position.columnIndex(); - strings[row][col] = outputConvertor.convertPieceTypeToString(pieceType); - } -} diff --git a/src/test/java/domain/BoardTest.java b/src/test/java/domain/BoardTest.java deleted file mode 100644 index 56028dccfc5..00000000000 --- a/src/test/java/domain/BoardTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package domain; - -import domain.board.File; -import domain.board.Position; -import domain.board.Rank; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static domain.TeamColor.WHITE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class BoardTest { - - @DisplayName("기물의 시작 위치를 배치한 Board 인스턴스를 생성한다.") - @Test - void createBoard() { - // When - Board board = BoardInitializer.init(); - - // Then - assertThat(board).isNotNull(); - } - - @DisplayName("기물의 이동 목적지가 비어있으면 이동시킬 수 있다.") - @Test - void movePieceToEmptySpaceTest() { - // Given - Piece piece = PieceFactory.create(PieceType.WHITE_BISHOP); - Position source = new Position(File.B, Rank.TWO); - Position destination = new Position(File.D, Rank.FOUR); - Map piecePositions = new HashMap<>(Map.of(source, piece)); - Board board = new Board(piecePositions); - - // When - board.movePiece(WHITE, source, destination); - - // Then - assertThat(piecePositions.containsKey(destination)).isTrue(); - assertThat(piecePositions.containsKey(source)).isFalse(); - } - - @DisplayName("기물의 이동 목적지에 다른 색의 기물이 있으면 이동시킬 수 있다.") - @Test - void movePieceToEnemySpaceTest() { - // Given - Piece piece = PieceFactory.create(PieceType.WHITE_KNIGHT); - Piece enemy = PieceFactory.create(PieceType.BLACK_KING); - Position source = new Position(File.B, Rank.TWO); - Position destination = new Position(File.C, Rank.FOUR); - Map piecePositions = new HashMap<>(Map.of( - source, piece, - destination, enemy - )); - Board board = new Board(piecePositions); - - // When - board.movePiece(WHITE, source, destination); - - // Then - assertThat(piecePositions.containsKey(destination)).isTrue(); - assertThat(piecePositions.containsKey(source)).isFalse(); - } - - @DisplayName("기물의 이동 목적지에 같은 색의 기물이 있으면 이동시킬 수 없다.") - @Test - void notMovePieceTest() { - // Given - Piece piece = PieceFactory.create(PieceType.WHITE_KNIGHT); - Piece other = PieceFactory.create(PieceType.WHITE_ROOK); - Position source = new Position(File.B, Rank.TWO); - Position destination = new Position(File.C, Rank.FOUR); - Map piecePositions = new HashMap<>(Map.of( - source, piece, - destination, other - )); - Board board = new Board(piecePositions); - - // When & Then - assertThatThrownBy(() -> board.movePiece(WHITE, source, destination)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("이동 위치에 아군 기물이 존재합니다."); - } -} diff --git a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java b/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java deleted file mode 100644 index f9bdbd42d09..00000000000 --- a/src/test/java/domain/strategy/ContinuousMoveStrategyTest.java +++ /dev/null @@ -1,137 +0,0 @@ -package domain.strategy; - -import domain.board.Position; -import domain.piece.CommonMovementDirection; -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.Collections; -import java.util.Set; -import java.util.stream.Stream; - -import static domain.board.File.*; -import static domain.board.Rank.*; -import static domain.piece.CommonMovementDirection.*; -import static org.assertj.core.api.Assertions.assertThat; - -class ContinuousMoveStrategyTest { - private static final Set orthogonalVectors = Set.of(UP, RIGHT, DOWN, LEFT); - private static final Set diagonalVectors = Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); - private static final Set omnidirectionalVectors = Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); - private static final ContinuousMoveStrategy orthogonalMoveStrategy = new ContinuousMoveStrategy(orthogonalVectors, 8); - private static final ContinuousMoveStrategy diagonalMoveStrategy = new ContinuousMoveStrategy(diagonalVectors, 8); - - @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 가능한 경우 true를 반환한다.") - @MethodSource("orthogonalValidMove") - @ParameterizedTest - void orthogonalMovableTest(Position source, Position destination) { - // Given - Set others = Collections.emptySet(); - - // When - boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isTrue(); - } - - private static Stream orthogonalValidMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(B, SEVEN)), - Arguments.of(new Position(D, THREE), new Position(F, THREE)), - Arguments.of(new Position(D, FIVE), new Position(D, TWO)), - Arguments.of(new Position(D, FIVE), new Position(A, FIVE)) - ); - } - - @DisplayName("출발지에서 목적지까지 상/하/좌/우 직선으로 이동 불가능한 경우 false를 반환한다.") - @MethodSource("orthogonalInvalidMove") - @ParameterizedTest - void notOrthogonalMovableTest(Position source, Position destination, Set others) { - // When - boolean movable = orthogonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isFalse(); - } - - private static Stream orthogonalInvalidMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(B, SEVEN), Set.of(new Position(B, FIVE))), - Arguments.of(new Position(D, THREE), new Position(F, THREE), Set.of(new Position(E, THREE))), - Arguments.of(new Position(D, FIVE), new Position(D, TWO), Set.of(new Position(D, THREE))), - Arguments.of(new Position(D, FIVE), new Position(A, FIVE), Set.of(new Position(C, FIVE))), - Arguments.of(new Position(D, FIVE), new Position(B, THREE), Collections.emptySet()) - ); - } - - @DisplayName("출발지에서 목적지까지 대각선으로 이동 가능한 경우 true를 반환한다.") - @MethodSource("diagonalValidMove") - @ParameterizedTest - void diagonalMovableTest(Position source, Position destination) { - // Given - Set others = Collections.emptySet(); - - // When - boolean movable = diagonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isTrue(); - } - - private static Stream diagonalValidMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(F, SEVEN)), - Arguments.of(new Position(D, THREE), new Position(F, ONE)), - Arguments.of(new Position(D, FIVE), new Position(A, TWO)), - Arguments.of(new Position(D, FIVE), new Position(C, SIX)) - ); - } - - @DisplayName("출발지에서 목적지까지 대각선으로 이동 불가능한 경우 false를 반환한다.") - @MethodSource("diagonalInvalidMove") - @ParameterizedTest - void notDiagonalMovableTest(Position source, Position destination, Set others) { - // When - boolean movable = diagonalMoveStrategy.isMovable(source, destination, others); - - // Then - assertThat(movable).isFalse(); - } - - private static Stream diagonalInvalidMove() { - return Stream.of( - Arguments.of(new Position(B, THREE), new Position(F, SEVEN), Set.of(new Position(D, FIVE))), - Arguments.of(new Position(D, THREE), new Position(F, ONE), Set.of(new Position(E, TWO))), - Arguments.of(new Position(D, FIVE), new Position(A, TWO), Set.of(new Position(B, THREE))), - Arguments.of(new Position(D, FIVE), new Position(B, SEVEN), Set.of(new Position(C, SIX))), - Arguments.of(new Position(D, FIVE), new Position(H, FIVE), Collections.emptySet()) - ); - } - - - @DisplayName("출발지에서 목적지까지 정해진 거리 이내에 이동이 불가능한 경우 false를 반환한다.") - @MethodSource("boundedMove") - @ParameterizedTest - void notDiagonalMovableTest(Position source, Position destination, int moveBound) { - // When - ContinuousMoveStrategy limitedBoundMoveStrategy = new ContinuousMoveStrategy(omnidirectionalVectors, moveBound); - boolean movable = limitedBoundMoveStrategy.isMovable(source, destination, Collections.emptySet()); - - // Then - assertThat(movable).isFalse(); - } - - private static Stream boundedMove() { - return Stream.of( - Arguments.of(new Position(D, FIVE), new Position(F, SEVEN), 1), - Arguments.of(new Position(D, FIVE), new Position(G, EIGHT), 2), - Arguments.of(new Position(A, EIGHT), new Position(E, FOUR), 3), - Arguments.of(new Position(A, EIGHT), new Position(A, THREE), 4), - Arguments.of(new Position(A, EIGHT), new Position(H, ONE), 5), - Arguments.of(new Position(A, EIGHT), new Position(H, EIGHT), 6) - ); - } -} diff --git a/src/test/java/domain/strategy/KnightMoveStrategyTest.java b/src/test/java/domain/strategy/KnightMoveStrategyTest.java deleted file mode 100644 index 25915134c48..00000000000 --- a/src/test/java/domain/strategy/KnightMoveStrategyTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package domain.strategy; - -import domain.board.File; -import domain.board.Position; -import domain.board.Rank; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; - -class KnightMoveStrategyTest { - - @DisplayName("나이트가 이동할 수 있는 출발지/도착지면 true를 반환한다.") - @Test - void isMovableTest() { - // Given - Position source = new Position(File.C, Rank.TWO); - Position destination = new Position(File.D, Rank.FOUR); - Set otherPositions = Set.of(new Position(File.C, Rank.THREE)); - KnightMoveStrategy knightMoveStrategy = new KnightMoveStrategy(); - - // When - boolean movable = knightMoveStrategy.isMovable(source, destination, otherPositions); - - // Then - assertThat(movable).isTrue(); - } - - @DisplayName("나이트가 이동할 수 없는 출발지/도착지면 false를 반환한다.") - @Test - void isNotMovableTest() { - // Given - Position source = new Position(File.C, Rank.TWO); - Position destination = new Position(File.G, Rank.FIVE); - Set otherPositions = Set.of(new Position(File.C, Rank.THREE)); - KnightMoveStrategy knightMoveStrategy = new KnightMoveStrategy(); - - // When - boolean movable = knightMoveStrategy.isMovable(source, destination, otherPositions); - - // Then - assertThat(movable).isFalse(); - } - -} diff --git a/src/test/java/domain/strategy/PawnMoveStrategyTest.java b/src/test/java/domain/strategy/PawnMoveStrategyTest.java deleted file mode 100644 index 7f7f092a5a5..00000000000 --- a/src/test/java/domain/strategy/PawnMoveStrategyTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package domain.strategy; - -import domain.TeamColor; -import domain.board.File; -import domain.board.Position; -import 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.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -class PawnMoveStrategyTest { - @DisplayName("폰이 이동할 수 있는 출발/도착 위치면 true를 반환한다.") - @MethodSource("isMovableCase") - @ParameterizedTest - void isMovableTest(TeamColor teamColor, Position source, Position destination) { - // Given - PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(teamColor); - Set otherPositions = Set.of(new Position(File.D, Rank.FIVE), new Position(File.B, Rank.FOUR)); - - // When - boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); - - // Then - assertThat(movable).isTrue(); - } - - private static Stream isMovableCase() { - return Stream.of( - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.TWO), new Position(File.C, Rank.FOUR)), - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.FIVE)), - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.D, Rank.FIVE)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.SEVEN), new Position(File.C, Rank.FIVE)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.C, Rank.FOUR)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FOUR)) - ); - } - - @DisplayName("폰이 이동할 수 없는 출발/도착 위치면 false를 반환한다.") - @MethodSource("isNotMovableCase") - @ParameterizedTest - void isNotMovableTest(TeamColor teamColor, Position source, Position destination) { - // Given - PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(teamColor); - Set otherPositions = Collections.emptySet(); - - // When - boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); - - // Then - assertThat(movable).isFalse(); - } - - private static Stream isNotMovableCase() { - return Stream.of( - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.SIX)), - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.C, Rank.THREE)), - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.D, Rank.FOUR)), - Arguments.of(TeamColor.WHITE, new Position(File.C, Rank.FOUR), new Position(File.B, Rank.FIVE)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.SIX), new Position(File.C, Rank.FOUR)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.C, Rank.SIX)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FIVE)), - Arguments.of(TeamColor.BLACK, new Position(File.C, Rank.FIVE), new Position(File.B, Rank.FOUR)) - ); - } - - @DisplayName("폰이 전진한 위치에 기물이 존재하면 이동할 수 없다.") - @Test - void isNotMovableToStraight() { - // Given - PawnMoveStrategy pawnMoveStrategy = new PawnMoveStrategy(TeamColor.WHITE); - Position source = new Position(File.B, Rank.TWO); - Position destination = new Position(File.B, Rank.THREE); - Set otherPositions = new HashSet<>(Set.of(destination)); - - // When - boolean movable = pawnMoveStrategy.isMovable(source, destination, otherPositions); - - // Then - assertThat(movable).isFalse(); - } - -} From c25b6477641217e306ec1526a2734ae1902f106a Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 14:13:55 +0900 Subject: [PATCH 44/54] =?UTF-8?q?refactor:=20piece=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/piece/Bishop.java | 3 +-- src/main/java/domain/piece/King.java | 3 +-- src/main/java/domain/piece/Knight.java | 3 +-- src/main/java/domain/piece/Pawn.java | 5 ++--- src/main/java/domain/piece/Piece.java | 1 - src/main/java/domain/piece/PieceColor.java | 10 +++++++++- src/main/java/domain/piece/PieceType.java | 10 ++++++++++ src/main/java/domain/piece/Queen.java | 3 +-- src/main/java/domain/piece/Rook.java | 3 +-- src/test/java/domain/piece/PawnTest.java | 2 +- 10 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 src/main/java/domain/piece/PieceType.java diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index a9a29ef127f..e13381d5834 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -1,13 +1,12 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.game.PieceType.BISHOP; +import static domain.piece.PieceType.BISHOP; import static domain.piece.CommonMovementDirection.*; public class Bishop extends Piece { diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java index 46a0c5e333a..760ee2f5e03 100644 --- a/src/main/java/domain/piece/King.java +++ b/src/main/java/domain/piece/King.java @@ -1,11 +1,10 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.Map; -import static domain.game.PieceType.KING; +import static domain.piece.PieceType.KING; import static domain.piece.CommonMovementDirection.calculateDirection; public class King extends Piece { diff --git a/src/main/java/domain/piece/Knight.java b/src/main/java/domain/piece/Knight.java index 04655b94ee9..fc9405c47cb 100644 --- a/src/main/java/domain/piece/Knight.java +++ b/src/main/java/domain/piece/Knight.java @@ -1,11 +1,10 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.Map; -import static domain.game.PieceType.KNIGHT; +import static domain.piece.PieceType.KNIGHT; import static domain.piece.KnightMovementDirection.calculateDirection; public class Knight extends Piece { diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java index 28284fe7d20..0aa1a1d8867 100644 --- a/src/main/java/domain/piece/Pawn.java +++ b/src/main/java/domain/piece/Pawn.java @@ -1,13 +1,12 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.Map; import static domain.board.Rank.SEVEN; import static domain.board.Rank.TWO; -import static domain.game.PieceType.PAWN; +import static domain.piece.PieceType.PAWN; import static domain.piece.PawnMovementDirection.calculateDirection; import static domain.piece.PieceColor.BLACK; import static domain.piece.PieceColor.WHITE; @@ -60,7 +59,7 @@ private void checkMovePaths( private void checkPathHasPiece(final Position path, final Map piecePositions) { if (piecePositions.containsKey(path)) { - throw new IllegalArgumentException("기물이 존재하는 칸으로 이동할 수 없습니다."); + throw new IllegalArgumentException("전진시 기물이 존재하는 경로 혹은 목적지로 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index c0769f2c903..70d377e2601 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -1,7 +1,6 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.Map; diff --git a/src/main/java/domain/piece/PieceColor.java b/src/main/java/domain/piece/PieceColor.java index 40fccd46e06..d4cb0e67321 100644 --- a/src/main/java/domain/piece/PieceColor.java +++ b/src/main/java/domain/piece/PieceColor.java @@ -1,5 +1,13 @@ package domain.piece; public enum PieceColor { - BLACK, WHITE + BLACK, WHITE; + + public PieceColor toggle() { + if (this == BLACK) { + return WHITE; + } + + return BLACK; + } } diff --git a/src/main/java/domain/piece/PieceType.java b/src/main/java/domain/piece/PieceType.java new file mode 100644 index 00000000000..3002a47b4e6 --- /dev/null +++ b/src/main/java/domain/piece/PieceType.java @@ -0,0 +1,10 @@ +package domain.piece; + +public enum PieceType { + PAWN, + ROOK, + KNIGHT, + BISHOP, + QUEEN, + KING +} diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index c343bdce5ac..0f939c520e9 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -1,13 +1,12 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.game.PieceType.QUEEN; +import static domain.piece.PieceType.QUEEN; import static domain.piece.CommonMovementDirection.*; public class Queen extends Piece { diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index 00eb7a209c9..d31611b91ff 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -1,13 +1,12 @@ package domain.piece; import domain.board.Position; -import domain.game.PieceType; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static domain.game.PieceType.*; +import static domain.piece.PieceType.*; import static domain.piece.CommonMovementDirection.*; public class Rook extends Piece { diff --git a/src/test/java/domain/piece/PawnTest.java b/src/test/java/domain/piece/PawnTest.java index 08f12a18550..78217af48f1 100644 --- a/src/test/java/domain/piece/PawnTest.java +++ b/src/test/java/domain/piece/PawnTest.java @@ -111,7 +111,7 @@ void throwExceptionWhenForwardPathHasPieceTest(final PieceColor pieceColor, fina // When & Then assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("기물이 존재하는 칸으로 이동할 수 없습니다."); + .hasMessage("전진시 기물이 존재하는 경로 혹은 목적지로 이동할 수 없습니다."); } private static Stream throwExceptionWhenForwardPathHasPieceTestCase() { From e9a1feb0e18dfbe7b55ffe4a4b519ed9b1d3aebf Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 14:14:39 +0900 Subject: [PATCH 45/54] =?UTF-8?q?refactor:=20board=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=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/domain/board/Board.java | 51 +++++++++ .../java/domain/board/BoardInitializer.java | 56 +++++++++ src/main/java/domain/game/ChessGame.java | 34 ++++++ src/main/java/domain/game/GameCommand.java | 13 +++ src/main/java/dto/BoardDto.java | 19 ++++ src/main/java/dto/PositionDto.java | 14 +++ src/test/java/domain/board/BoardTest.java | 107 ++++++++++++++++++ 7 files changed, 294 insertions(+) create mode 100644 src/main/java/domain/board/Board.java create mode 100644 src/main/java/domain/board/BoardInitializer.java create mode 100644 src/main/java/domain/game/ChessGame.java create mode 100644 src/main/java/domain/game/GameCommand.java create mode 100644 src/main/java/dto/BoardDto.java create mode 100644 src/main/java/dto/PositionDto.java create mode 100644 src/test/java/domain/board/BoardTest.java diff --git a/src/main/java/domain/board/Board.java b/src/main/java/domain/board/Board.java new file mode 100644 index 00000000000..f67e07aa0eb --- /dev/null +++ b/src/main/java/domain/board/Board.java @@ -0,0 +1,51 @@ +package domain.board; + +import domain.piece.Piece; +import domain.piece.PieceColor; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class Board { + private final Map piecePositions; + + public Board(final Map piecePositions) { + this.piecePositions = piecePositions; + } + + public void movePiece(final PieceColor pieceColor, final Position source, final Position destination) { + validatePosition(pieceColor, source, destination); + + Piece targetPiece = piecePositions.get(source); + targetPiece.checkMovable(source, destination, parsePiecePositionsIgnoreTargetPiece(source)); + + piecePositions.put(destination, targetPiece); + piecePositions.remove(source); + } + + private void validatePosition(final PieceColor pieceColor, final Position source, final Position destination) { + if (source.equals(destination)) { + throw new IllegalArgumentException("출발지와 목적지가 같을 수 없습니다."); + } + + if (!piecePositions.containsKey(source)) { + throw new IllegalArgumentException("출발지에 기물이 존재하지 않습니다."); + } + + if (piecePositions.get(source).isEnemy(pieceColor)) { + throw new IllegalArgumentException("상대방의 기물을 이동시킬 수 없습니다."); + } + } + + private Map parsePiecePositionsIgnoreTargetPiece(final Position targetPiecePosition) { + Map piecePositionsIgnoreTargetPiece = new HashMap<>(piecePositions); + piecePositionsIgnoreTargetPiece.remove(targetPiecePosition); + + return piecePositionsIgnoreTargetPiece; + } + + public Map piecePositions() { + return Collections.unmodifiableMap(piecePositions); + } +} diff --git a/src/main/java/domain/board/BoardInitializer.java b/src/main/java/domain/board/BoardInitializer.java new file mode 100644 index 00000000000..bfdcab2cb02 --- /dev/null +++ b/src/main/java/domain/board/BoardInitializer.java @@ -0,0 +1,56 @@ +package domain.board; + +import domain.piece.*; + +import java.util.HashMap; +import java.util.Map; + +import static domain.board.File.*; +import static domain.board.Rank.*; +import static domain.piece.PieceColor.BLACK; +import static domain.piece.PieceColor.WHITE; + +public class BoardInitializer { + private static final Map initialPiecePositions = Map.ofEntries( + Map.entry(position(E, ONE), new King(WHITE)), + Map.entry(position(D, ONE), new Queen(WHITE)), + Map.entry(position(C, ONE), new Bishop(WHITE)), + Map.entry(position(F, ONE), new Bishop(WHITE)), + Map.entry(position(B, ONE), new Knight(WHITE)), + Map.entry(position(G, ONE), new Knight(WHITE)), + Map.entry(position(A, ONE), new Rook(WHITE)), + Map.entry(position(H, ONE), new Rook(WHITE)), + Map.entry(position(A, TWO), new Pawn(WHITE)), + Map.entry(position(B, TWO), new Pawn(WHITE)), + Map.entry(position(C, TWO), new Pawn(WHITE)), + Map.entry(position(D, TWO), new Pawn(WHITE)), + Map.entry(position(E, TWO), new Pawn(WHITE)), + Map.entry(position(F, TWO), new Pawn(WHITE)), + Map.entry(position(G, TWO), new Pawn(WHITE)), + Map.entry(position(H, TWO), new Pawn(WHITE)), + Map.entry(position(E, EIGHT), new King(BLACK)), + Map.entry(position(D, EIGHT), new Queen(BLACK)), + Map.entry(position(C, EIGHT), new Bishop(BLACK)), + Map.entry(position(F, EIGHT), new Bishop(BLACK)), + Map.entry(position(B, EIGHT), new Knight(BLACK)), + Map.entry(position(G, EIGHT), new Knight(BLACK)), + Map.entry(position(A, EIGHT), new Rook(BLACK)), + Map.entry(position(H, EIGHT), new Rook(BLACK)), + Map.entry(position(A, SEVEN), new Pawn(BLACK)), + Map.entry(position(B, SEVEN), new Pawn(BLACK)), + Map.entry(position(C, SEVEN), new Pawn(BLACK)), + Map.entry(position(D, SEVEN), new Pawn(BLACK)), + Map.entry(position(E, SEVEN), new Pawn(BLACK)), + Map.entry(position(F, SEVEN), new Pawn(BLACK)), + Map.entry(position(G, SEVEN), new Pawn(BLACK)), + Map.entry(position(H, SEVEN), new Pawn(BLACK)) + ); + + public static Board initBoard() { + return new Board(new HashMap<>(initialPiecePositions)); + } + + private static Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} diff --git a/src/main/java/domain/game/ChessGame.java b/src/main/java/domain/game/ChessGame.java new file mode 100644 index 00000000000..33aa419f760 --- /dev/null +++ b/src/main/java/domain/game/ChessGame.java @@ -0,0 +1,34 @@ +package domain.game; + +import domain.board.Board; +import domain.board.BoardInitializer; +import domain.board.Position; +import domain.piece.Piece; +import domain.piece.PieceColor; + +import java.util.Map; + +import static domain.piece.PieceColor.WHITE; + +public class ChessGame { + private final Board board; + + private PieceColor currentColor = WHITE; + + public ChessGame(final Board board) { + this.board = board; + } + + public static ChessGame initGame() { + return new ChessGame(BoardInitializer.initBoard()); + } + + public Map piecePositions() { + return board.piecePositions(); + } + + public void play(final Position source, final Position destination) { + board.movePiece(currentColor, source, destination); + currentColor = currentColor.toggle(); + } +} diff --git a/src/main/java/domain/game/GameCommand.java b/src/main/java/domain/game/GameCommand.java new file mode 100644 index 00000000000..33fe738d19e --- /dev/null +++ b/src/main/java/domain/game/GameCommand.java @@ -0,0 +1,13 @@ +package domain.game; + +public enum GameCommand { + START, END, MOVE; + + public boolean isStartCommand() { + return this == START; + } + + public boolean isMoveCommand() { + return this == MOVE; + } +} diff --git a/src/main/java/dto/BoardDto.java b/src/main/java/dto/BoardDto.java new file mode 100644 index 00000000000..c41de62ca33 --- /dev/null +++ b/src/main/java/dto/BoardDto.java @@ -0,0 +1,19 @@ +package dto; + +import domain.board.Position; +import domain.piece.Piece; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public record BoardDto(Map value) { + + public static BoardDto from(Map piecePositions) { + Map value = new HashMap<>(); + piecePositions.forEach(((position, piece) -> + value.put(PositionDto.from(position), PieceDto.from(piece)))); + + return new BoardDto(Collections.unmodifiableMap(value)); + } +} diff --git a/src/main/java/dto/PositionDto.java b/src/main/java/dto/PositionDto.java new file mode 100644 index 00000000000..6f4eb347c09 --- /dev/null +++ b/src/main/java/dto/PositionDto.java @@ -0,0 +1,14 @@ +package dto; + +import domain.board.Position; + +public record PositionDto(int row, int column) { + + public static PositionDto from(final Position position) { + return new PositionDto(position.rowIndex(), position.columnIndex()); + } + + public static PositionDto emptyPosition() { + return new PositionDto(0, 0); + } +} diff --git a/src/test/java/domain/board/BoardTest.java b/src/test/java/domain/board/BoardTest.java new file mode 100644 index 00000000000..3afc8f424ab --- /dev/null +++ b/src/test/java/domain/board/BoardTest.java @@ -0,0 +1,107 @@ +package domain.board; + +import domain.piece.Pawn; +import domain.piece.Piece; +import domain.piece.Rook; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static domain.board.File.*; +import static domain.board.Rank.*; +import static domain.piece.PieceColor.BLACK; +import static domain.piece.PieceColor.WHITE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BoardTest { + + @DisplayName("기물을 이동시킨다.") + @Test + void movePieceTest() { + // Given + Position source = position(B, TWO); + Position destination = position(B, FOUR); + Map piecePositions = new HashMap<>(Map.of(source, new Pawn(WHITE))); + Board board = new Board(piecePositions); + + // When + board.movePiece(WHITE, source, destination); + boolean existsPieceOnSource = piecePositions.containsKey(source); + boolean existsPieceOnDestination = piecePositions.containsKey(destination); + + // Then + assertThat(existsPieceOnSource).isFalse(); + assertThat(existsPieceOnDestination).isTrue(); + } + + @DisplayName("목적지에 적 기물이 존재하면 적 기물을 제거하고 이동시킨다.") + @Test + void movePieceWithRemoveEnemyPieceTest() { + // Given + Position source = position(B, TWO); + Position destination = position(B, FOUR); + Rook myPiece = new Rook(WHITE); + Pawn enemyPiece = new Pawn(BLACK); + Map piecePositions = new HashMap<>(Map.of(source, myPiece, destination, enemyPiece)); + Board board = new Board(piecePositions); + + // When + board.movePiece(WHITE, source, destination); + boolean existsPieceOnSource = piecePositions.containsKey(source); + Piece destinationPiece = piecePositions.get(destination); + + // Then + assertThat(existsPieceOnSource).isFalse(); + assertThat(destinationPiece).isNotEqualTo(enemyPiece); + assertThat(destinationPiece).isEqualTo(myPiece); + } + + @DisplayName("입력된 출발지와 목적지의 위치가 같으면 예외를 발생시킨다.") + @Test + void throwExceptionWhenSourceEqualsDestinationTest() { + // Given + Board board = BoardInitializer.initBoard(); + Position source = new Position(B, TWO); + Position destination = new Position(B, TWO); + + // When & Then + assertThatThrownBy(() -> board.movePiece(WHITE, source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지와 목적지가 같을 수 없습니다."); + } + + @DisplayName("기물이 존재하지 않는 출발지 위치가 입력되면 예외를 발생시킨다.") + @Test + void throwExceptionWhenNotExistPieceSourceTest() { + // Given + Board board = BoardInitializer.initBoard(); + Position source = new Position(D, FIVE); + Position destination = new Position(B, TWO); + + // When & Then + assertThatThrownBy(() -> board.movePiece(WHITE, source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("출발지에 기물이 존재하지 않습니다."); + } + + @DisplayName("상대방의 기물을 이동시키려고 하면 예외를 발생시킨다.") + @Test + void throwExceptionWhenMoveEnemyPieceTest() { + // Given + Board board = BoardInitializer.initBoard(); + Position source = new Position(B, SEVEN); + Position destination = new Position(B, SIX); + + // When & Then + assertThatThrownBy(() -> board.movePiece(WHITE, source, destination)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("상대방의 기물을 이동시킬 수 없습니다."); + } + + private Position position(final File file, final Rank rank) { + return new Position(file, rank); + } +} From 225b8d5745d5e1a6015b408063cc869da98fae18 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 14:15:01 +0900 Subject: [PATCH 46/54] =?UTF-8?q?refactor:=20view=20&=20dto=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 12 ++ src/main/java/controller/GameController.java | 73 ++++++++++++ src/main/java/dto/MoveRequestDto.java | 35 ++++++ src/main/java/dto/PieceDto.java | 12 ++ src/main/java/view/InputView.java | 115 +++++++++++++++++++ src/main/java/view/OutputConvertor.java | 28 ----- src/main/java/view/OutputView.java | 75 ++++++++++++ 7 files changed, 322 insertions(+), 28 deletions(-) create mode 100644 src/main/java/Application.java create mode 100644 src/main/java/controller/GameController.java create mode 100644 src/main/java/dto/MoveRequestDto.java create mode 100644 src/main/java/dto/PieceDto.java create mode 100644 src/main/java/view/InputView.java delete mode 100644 src/main/java/view/OutputConvertor.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000000..ff3accb42d4 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,12 @@ +import controller.GameController; +import domain.game.ChessGame; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + ChessGame chessGame = ChessGame.initGame(); + GameController gameController = new GameController(new InputView(), new OutputView(), chessGame); + gameController.run(); + } +} diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000000..f1c4312b045 --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,73 @@ +package controller; + +import domain.game.*; +import dto.BoardDto; +import dto.MoveRequestDto; +import view.InputView; +import view.OutputView; + +public class GameController { + private final InputView inputView; + private final OutputView outputView; + private final ChessGame chessGame; + + public GameController(final InputView inputView, final OutputView outputView, final ChessGame chessGame) { + this.inputView = inputView; + this.outputView = outputView; + this.chessGame = chessGame; + } + + public void run() { + GameCommand gameCommand = inputGameStartCommand(); + if (gameCommand.isStartCommand()) { + gameStart(); + } + } + + private GameCommand inputGameStartCommand() { + try { + return inputView.inputGameStartCommand(); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + return inputGameStartCommand(); + } + } + + private void gameStart() { + outputView.printWelcomeMessage(); + boolean isContinuable = true; + + while (isContinuable) { + BoardDto boardDto = BoardDto.from(chessGame.piecePositions()); + outputView.printBoard(boardDto); + + isContinuable = playTurn(); + } + } + + private MoveRequestDto inputMoveRequest() { + try { + return inputView.inputMoveRequest(); + } catch (Exception e) { + outputView.printErrorMessage(e.getMessage()); + + return inputMoveRequest(); + } + } + + private boolean playTurn() { + MoveRequestDto moveRequest = inputMoveRequest(); + if (moveRequest.isEndCommand()) { + return false; + } + + try { + chessGame.play(moveRequest.sourcePosition(), moveRequest.destinationPosition()); + return true; + } catch (Exception e) { + outputView.printErrorMessage(e.getMessage()); + + return playTurn(); + } + } +} diff --git a/src/main/java/dto/MoveRequestDto.java b/src/main/java/dto/MoveRequestDto.java new file mode 100644 index 00000000000..e4afba7cdc6 --- /dev/null +++ b/src/main/java/dto/MoveRequestDto.java @@ -0,0 +1,35 @@ +package dto; + +import domain.board.File; +import domain.board.Position; +import domain.board.Rank; +import domain.game.GameCommand; + +public record MoveRequestDto(GameCommand gameCommand, PositionDto source, PositionDto destination) { + + public static MoveRequestDto of(final GameCommand gameCommand, final PositionDto source, final PositionDto destination) { + return new MoveRequestDto(gameCommand, source, destination); + } + + public static MoveRequestDto of(final GameCommand gameCommand) { + return new MoveRequestDto(gameCommand, PositionDto.emptyPosition(), PositionDto.emptyPosition()); + } + + public boolean isEndCommand() { + return !gameCommand.isMoveCommand(); + } + + public Position sourcePosition() { + File file = File.of(source.row()); + Rank rank = Rank.of(source.column()); + + return new Position(file, rank); + } + + public Position destinationPosition() { + File file = File.of(destination.row()); + Rank rank = Rank.of(destination.column()); + + return new Position(file, rank); + } +} diff --git a/src/main/java/dto/PieceDto.java b/src/main/java/dto/PieceDto.java new file mode 100644 index 00000000000..66a33e05564 --- /dev/null +++ b/src/main/java/dto/PieceDto.java @@ -0,0 +1,12 @@ +package dto; + +import domain.piece.PieceType; +import domain.piece.Piece; +import domain.piece.PieceColor; + +public record PieceDto(PieceType type, PieceColor color) { + + public static PieceDto from(final Piece piece) { + return new PieceDto(piece.pieceType(), piece.pieceColor()); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000000..7592ee3fb0b --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,115 @@ +package view; + +import domain.game.GameCommand; +import dto.MoveRequestDto; +import dto.PositionDto; + +import java.util.Map; +import java.util.Scanner; + +public class InputView { + private static final String START_COMMAND = "start"; + private static final String MOVE_COMMAND = "move"; + private static final String END_COMMAND = "end"; + private static final Map gameCommands = Map.of( + START_COMMAND, GameCommand.START, + MOVE_COMMAND, GameCommand.MOVE, + END_COMMAND, GameCommand.END + ); + private static final Map fileIndexes = Map.ofEntries( + Map.entry("a", 0), + Map.entry("b", 1), + Map.entry("c", 2), + Map.entry("d", 3), + Map.entry("e", 4), + Map.entry("f", 5), + Map.entry("g", 6), + Map.entry("h", 7), + Map.entry("8", 0), + Map.entry("7", 1), + Map.entry("6", 2), + Map.entry("5", 3), + Map.entry("4", 4), + Map.entry("3", 5), + Map.entry("2", 6), + Map.entry("1", 7) + ); + + private final Scanner sc = new Scanner(System.in); + + public GameCommand inputGameStartCommand() { + String input = sc.nextLine(); + validateInputStringEmpty(input); + validateInputCommand(input); + + return gameCommands.get(input); + } + + private void validateInputStringEmpty(final String input) { + if (input == null || input.isBlank()) { + throw new IllegalArgumentException("공백을 입력할 수 없습니다."); + } + + if (input.equals(MOVE_COMMAND)) { + throw new IllegalArgumentException("START 혹은 END 명령만 입력할 수 있습니다."); + } + } + + private void validateInputCommand(final String input) { + if (!gameCommands.containsKey(input)) { + throw new IllegalArgumentException("존재하지 않는 명령입니다."); + } + } + + public MoveRequestDto inputMoveRequest() { + String input = sc.nextLine(); + validateInputStringEmpty(input); + + String[] inputStrings = input.split(" "); + validateInputCommand(inputStrings); + + GameCommand gameCommand = gameCommands.get(inputStrings[0]); + if (!gameCommand.isMoveCommand()) { + return MoveRequestDto.of(gameCommand); + } + + PositionDto source = convertInputToPosition(inputStrings[1]); + PositionDto destination = convertInputToPosition(inputStrings[2]); + + return new MoveRequestDto(gameCommand, source, destination); + } + + private void validateInputCommand(final String[] input) { + if (input.length != 1 && input.length != 3) { + throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); + } + + if (!gameCommands.containsKey(input[0])) { + throw new IllegalArgumentException("존재하지 않는 명령입니다."); + } + + if (input[0].equals(START_COMMAND)) { + throw new IllegalArgumentException("MOVE 혹은 END 명령만 입력할 수 있습니다."); + } + } + + private PositionDto convertInputToPosition(final String input) { + if (input.length() != 2) { + throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); + } + + String[] inputStrings = input.split(""); + int rowIndex = parseIndex(inputStrings[0]); + int columnIndex = parseIndex(inputStrings[1]); + + return new PositionDto(rowIndex, columnIndex); + } + + private int parseIndex(final String input) { + if (!fileIndexes.containsKey(input.toLowerCase())) { + throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); + } + + return fileIndexes.get(input.toLowerCase()); + } +} diff --git a/src/main/java/view/OutputConvertor.java b/src/main/java/view/OutputConvertor.java deleted file mode 100644 index f6828b7e550..00000000000 --- a/src/main/java/view/OutputConvertor.java +++ /dev/null @@ -1,28 +0,0 @@ -package view; - -import domain.PieceType; - -import java.util.Map; - -import static domain.PieceType.*; - -public class OutputConvertor { - private static final Map pieceFormat = Map.ofEntries( - Map.entry(BLACK_ROOK, "R"), - Map.entry(BLACK_KNIGHT, "N"), - Map.entry(BLACK_BISHOP, "B"), - Map.entry(BLACK_QUEEN, "Q"), - Map.entry(BLACK_KING, "K"), - Map.entry(BLACK_PAWN, "P"), - Map.entry(WHITE_ROOK, "r"), - Map.entry(WHITE_KNIGHT, "n"), - Map.entry(WHITE_BISHOP, "b"), - Map.entry(WHITE_QUEEN, "q"), - Map.entry(WHITE_KING, "k"), - Map.entry(WHITE_PAWN, "p") - ); - - public String convertPieceTypeToString(final PieceType pieceType) { - return pieceFormat.get(pieceType); - } -} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000000..0860a5ff55d --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,75 @@ +package view; + +import domain.piece.PieceColor; +import domain.piece.PieceType; +import dto.BoardDto; +import dto.PieceDto; +import dto.PositionDto; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static domain.piece.PieceType.*; +import static domain.piece.PieceType.PAWN; + +public class OutputView { + private static final Map pieceFormat = Map.ofEntries( + Map.entry(ROOK, "R"), + Map.entry(KNIGHT, "N"), + Map.entry(BISHOP, "B"), + Map.entry(QUEEN, "Q"), + Map.entry(KING, "K"), + Map.entry(PAWN, "P") + ); + + public void printErrorMessage(final String message) { + System.out.println(message); + System.out.println(); + } + + public void printWelcomeMessage() { + System.out.println("> 체스 게임을 시작합니다."); + System.out.println("> 게임 시작: start"); + System.out.println("> 게임 종료: end"); + System.out.println("> 게임 이동: move source위치 target위치 - 예. move b2 b3"); + } + + public void printBoard(final BoardDto boardDto) { + List boardStatus = convertBoardStatus(boardDto.value()); + boardStatus.forEach(System.out::println); + System.out.println(); + } + + private List convertBoardStatus(final Map boardStatus) { + String[][] boardMessage = initEmptyBoardMessage(); + boardStatus.forEach((position, piece) -> addPieceStatusInBoardMessage(boardMessage, position, piece)); + + return Arrays.stream(boardMessage) + .map(rowString -> String.join("", rowString)) + .toList(); + } + + private static String[][] initEmptyBoardMessage() { + String[][] strings = new String[8][8]; + for (String[] row : strings) { + Arrays.fill(row, "."); + } + + return strings; + } + + private void addPieceStatusInBoardMessage(final String[][] boardMessage, final PositionDto position, final PieceDto piece) { + boardMessage[position.row()][position.column()] = convertPieceTypeToString(piece.type(), piece.color()); + } + + private String convertPieceTypeToString(final PieceType pieceType, final PieceColor pieceColor) { + String pieceMessage = pieceFormat.get(pieceType); + + if (pieceColor == PieceColor.WHITE) { + return pieceMessage.toLowerCase(); + } + + return pieceMessage; + } +} From 42dab99fd98a1f2639c755d0ee009440519aa01c Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Sun, 24 Mar 2024 14:38:13 +0900 Subject: [PATCH 47/54] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EC=84=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 105 ++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/docs/README.md b/docs/README.md index e87941958af..8ad0f4d0ad9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,58 +1,63 @@ -## 기능 흐름 - -- [x] 사용자로부터 게임 시작 여부 입력 - - [x] `start`를 입력받으면 체스판을 초기화한다. - - [x] `end`를 입력받으면 프로그램을 종료한다. - - [ ] `move`와 출발지/목적지 정보를 입력받으면 기물 이동을 시도한다. -- [x] 체스판을 초기화한다. -- [x] 체스판을 출력한다. -- [ ] 체스 기물을 이동시킨다. - - [ ] 이동이 가능 여부를 확인한다. - - [ ] 기물 위치를 업데이트한다. +## 프로그램 흐름 + +- 사용자로부터 게임 시작 여부를 입력받는다. + - `start`를 입력받으면 게임을 생성하고 초기 체스판의 상태를 출력한다. + - `end`를 입력받으면 프로그램을 종료한다. +- 기물 이동 여부를 입력받는다. + - `move b2 b4`와 같이 입력받으면 `b2`에 존재하는 기물을 `b4`로 이동시킨다. + - `end`를 입력받으면 프로그램을 종료한다. ## 도메인별 기능 -### Vector +### Piece + +- [x] 출발지 & 목적지 정보를 토대로 기물이 이동할 수 있는지 검사한다. +- [x] 입력받은 색상을 토대로 자신의 적 여부를 반환한다. -- [ ] 행, 열의 두 정수 성분을 가지고, 단위벡터로서 이동의 방향성을 나타낸다. -- [ ] 출발지, 도착지의 위치 정보를 받아서 해당 방향으로 이동하기 위한 최적의 벡터 객체를 생성한다. +#### Rook -### Position +- 상/하/좌/우 거리 제한 없이 이동 가능 +- 비었거나 적 기물이 존재하는 칸으로만 이동 가능 +- 이동 경로에 기물이 존재하면 이동 불가 -- [ ] `Rank`, `File`의 쌍을 가지고 한 칸의 위치를 표현한다. +#### Bishop -### Piece +- 대각선 방향 거리 제한 없이 이동 가능 +- 비었거나 적 기물이 존재하는 칸으로만 이동 가능 +- 이동 경로에 기물이 존재하면 이동 불가 + +#### Queen + +- 상/하/좌/우, 대각선 방향 거리 제한 없이 이동 가능 +- 비었거나 적 기물이 존재하는 칸으로만 이동 가능 +- 이동 경로에 기물이 존재하면 이동 불가 + +#### King + +- 상/하/좌/우, 대각선 방향 중 한 칸만 이동 가능 +- 비었거나 적 기물이 존재하는 칸으로만 이동 가능 + +#### Knight + +- 상/하/좌/우 중 한 칸 이동 후 전진 방향의 대각석으로 한 칸 이동 가능 +- 비었거나 적 기물이 존재하는 칸으로만 이동 가능 +- 이동 경로에 기물이 존재해도 이동 가능 + +#### Pawn + +- 특정 조건에 따라 이동 선택 + - 기본적으로 한 칸 전진 가능 + - 출발지가 초기 위치일 경우 두 칸 전진 가능 + - 전진 경로 혹은 목적지에 기물이 존재하면 이동 불가 + - 전진 방향 대각선 한 칸에 적 기물이 존재시 이동 가능 + +## Board + +- [x] 특정 색상의 기물 이동 + - `출발지` == `도착지`인 경우 예외 처리 + - 출발지에 기물이 존재하지 않으면 예외 처리 + - 상대 색상의 기물을 이동시키려 할 시 예외 처리 + +## ChessGame -- [ ] 타입이 다른 기물들과 자신을 구별할 수 있는`PieceType`을 가진다. -- [ ] 이동 가능한 위치인지 여부를 확인한다. -- [ ] 모든 말을 체스보드 안에서만 움직일 수 있다. -- [ ] 목적지까지 가는 경로에 다른 기물이 존재하면 이동할 수 없다. - - 록 - - [ ] 동서남북 방향으로 직진할 수 있다. - - 비숍 - - [ ] 대각선 방향으로 직진할 수 있다. - - 퀸 - - [ ] 동서남북, 혹은 대각선 방향으로 직진할 수 있다. - - 킹 - - [ ] 동서남북, 혹은 대각선 방향으로 1칸만 이동할 수 있다. - - 나이트 - - [ ] 동서남북 방향으로 한 칸 전진 후, 전진한 방향에서 대각선으로 한 칸 이동할 수 있다. - - [ ] 다른 말을 뛰어넘을 수 있다. - - 폰 - - [ ] 한 칸만 앞으로 전진만 할 수 있다. - - [ ] 다른 말을 공격하는 경우 대각선 방향으로 한 칸만 이동할 수 있다. - - [ ] 최초로 이동할 경우 한 칸 또는 두 칸 이동할 수 있다. - -### Board - -- [ ] 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다. -- [ ] 기물의 도착지에 현재 이동하는 기물과 같은 색의 기물이 존재하면 이동할 수 없다. -- [ ] 도착지에 다른 색의 기물이 존재하는 경우, 해당 기물을 제거하고 배치한다. - -### InputView - -- [ ] `start` 명령과 `end`, `move` 명령을 입력받는다. - -### OutputView - -- [ ] 초기화 된 체스판을 출력한다. +- [x] `흰색` 플레이어와 `검은색` 플레이어의 게임 순서 관리 및 `Board`에 기물 이동 요청 전달 From 10efcaa1219e7079670281eb7955fcc218606e09 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 02:39:06 +0900 Subject: [PATCH 48/54] =?UTF-8?q?refactor:=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=ED=8E=98=ED=84=B4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 55 ++++------ src/main/java/domain/board/File.java | 7 ++ src/main/java/domain/board/Position.java | 19 ++++ src/main/java/domain/board/Rank.java | 43 ++++++-- src/main/java/domain/game/ChessGame.java | 17 ++- src/main/java/domain/game/End.java | 9 ++ src/main/java/domain/game/GameCommand.java | 12 +- .../java/domain/game/GameCommandType.java | 48 ++++++++ src/main/java/domain/game/Move.java | 22 ++++ src/main/java/domain/game/Start.java | 13 +++ src/main/java/dto/MoveRequestDto.java | 35 ------ src/main/java/view/InputView.java | 103 +----------------- src/main/java/view/PositionConvertor.java | 38 ------- 13 files changed, 193 insertions(+), 228 deletions(-) create mode 100644 src/main/java/domain/game/End.java create mode 100644 src/main/java/domain/game/GameCommandType.java create mode 100644 src/main/java/domain/game/Move.java create mode 100644 src/main/java/domain/game/Start.java delete mode 100644 src/main/java/dto/MoveRequestDto.java delete mode 100644 src/main/java/view/PositionConvertor.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index f1c4312b045..605348a46cf 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,8 +1,9 @@ package controller; -import domain.game.*; +import domain.game.ChessGame; +import domain.game.GameCommand; +import domain.game.GameCommandType; import dto.BoardDto; -import dto.MoveRequestDto; import view.InputView; import view.OutputView; @@ -18,56 +19,44 @@ public GameController(final InputView inputView, final OutputView outputView, fi } public void run() { - GameCommand gameCommand = inputGameStartCommand(); - if (gameCommand.isStartCommand()) { + initGame(); + + if (chessGame.isGameRunning()) { gameStart(); } } - private GameCommand inputGameStartCommand() { + private void initGame() { try { - return inputView.inputGameStartCommand(); - } catch (IllegalArgumentException e) { + GameCommand gameCommand = inputCommand(); + gameCommand.execute(chessGame); + } catch (Exception e) { outputView.printErrorMessage(e.getMessage()); - return inputGameStartCommand(); + initGame(); } } + private GameCommand inputCommand() { + String[] inputValues = inputView.inputCommand().split(" "); + return GameCommandType.of(inputValues); + } + private void gameStart() { outputView.printWelcomeMessage(); - boolean isContinuable = true; - - while (isContinuable) { + while (chessGame.isGameRunning()) { BoardDto boardDto = BoardDto.from(chessGame.piecePositions()); outputView.printBoard(boardDto); - - isContinuable = playTurn(); - } - } - - private MoveRequestDto inputMoveRequest() { - try { - return inputView.inputMoveRequest(); - } catch (Exception e) { - outputView.printErrorMessage(e.getMessage()); - - return inputMoveRequest(); + playTurn(); } } - private boolean playTurn() { - MoveRequestDto moveRequest = inputMoveRequest(); - if (moveRequest.isEndCommand()) { - return false; - } - + private void playTurn() { try { - chessGame.play(moveRequest.sourcePosition(), moveRequest.destinationPosition()); - return true; + GameCommand gameCommand = inputCommand(); + gameCommand.execute(chessGame); } catch (Exception e) { outputView.printErrorMessage(e.getMessage()); - - return playTurn(); + playTurn(); } } } diff --git a/src/main/java/domain/board/File.java b/src/main/java/domain/board/File.java index 146a1102098..f4504ddfb76 100644 --- a/src/main/java/domain/board/File.java +++ b/src/main/java/domain/board/File.java @@ -18,6 +18,13 @@ public enum File { this.index = index; } + public static File of(final String value) { + return Arrays.stream(values()) + .filter(file -> file.name().equals(value.toUpperCase())) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 File 값입니다.")); + } + public static File of(final int index) { return Arrays.stream(values()) .filter(file -> file.getIndex() == index) diff --git a/src/main/java/domain/board/Position.java b/src/main/java/domain/board/Position.java index 6536da81cbf..51b2270f99f 100644 --- a/src/main/java/domain/board/Position.java +++ b/src/main/java/domain/board/Position.java @@ -3,6 +3,25 @@ import domain.piece.MovementDirection; public record Position(File file, Rank rank) { + private static final int POSITIONS_VALUES_SIZE = 2; + private static final int FILE_INDEX = 0; + private static final int RANK_INDEX = 1; + + public static Position of(final String value) { + String[] positionValues = value.split(""); + validatePositionValue(positionValues); + + File file = File.of(positionValues[FILE_INDEX]); + Rank rank = Rank.of(positionValues[RANK_INDEX]); + + return new Position(file, rank); + } + + private static void validatePositionValue(final String[] values) { + if (values.length != POSITIONS_VALUES_SIZE) { + throw new IllegalArgumentException("잘못된 위치값입니다."); + } + } public Position next(final MovementDirection movementDirection) { File nextFile = File.of(this.columnIndex() + movementDirection.getColumnDistance()); diff --git a/src/main/java/domain/board/Rank.java b/src/main/java/domain/board/Rank.java index fb82e012bc4..32697ddf3b7 100644 --- a/src/main/java/domain/board/Rank.java +++ b/src/main/java/domain/board/Rank.java @@ -3,19 +3,36 @@ import java.util.Arrays; public enum Rank { - ONE(7), - TWO(6), - THREE(5), + ONE(1), + TWO(2), + THREE(3), FOUR(4), - FIVE(3), - SIX(2), - SEVEN(1), - EIGHT(0); + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8); - private final int index; + private static final int BASE_INDEX = 8; - Rank(final int index) { - this.index = index; + private final int value; + + Rank(final int value) { + this.value = value; + } + + public static Rank of(final String value) { + return Arrays.stream(Rank.values()) + .filter(rank -> rank.value() == parseNumber(value)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 Rank값입니다.")); + } + + private static int parseNumber(final String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Rank는 정수만 입력할 수 있습니다."); + } } public static Rank of(final int index) { @@ -25,7 +42,11 @@ public static Rank of(final int index) { .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 인덱스입니다.")); } + public int value() { + return value; + } + public int getIndex() { - return index; + return BASE_INDEX - value; } } diff --git a/src/main/java/domain/game/ChessGame.java b/src/main/java/domain/game/ChessGame.java index 33aa419f760..837e3c9595e 100644 --- a/src/main/java/domain/game/ChessGame.java +++ b/src/main/java/domain/game/ChessGame.java @@ -14,8 +14,9 @@ public class ChessGame { private final Board board; private PieceColor currentColor = WHITE; + private boolean isGameRunning = false; - public ChessGame(final Board board) { + private ChessGame(final Board board) { this.board = board; } @@ -23,11 +24,23 @@ public static ChessGame initGame() { return new ChessGame(BoardInitializer.initBoard()); } + public void gameStart() { + isGameRunning = true; + } + + public void gameEnd() { + isGameRunning = false; + } + + public boolean isGameRunning() { + return isGameRunning; + } + public Map piecePositions() { return board.piecePositions(); } - public void play(final Position source, final Position destination) { + public void movePiece(final Position source, final Position destination) { board.movePiece(currentColor, source, destination); currentColor = currentColor.toggle(); } diff --git a/src/main/java/domain/game/End.java b/src/main/java/domain/game/End.java new file mode 100644 index 00000000000..0e381c82c82 --- /dev/null +++ b/src/main/java/domain/game/End.java @@ -0,0 +1,9 @@ +package domain.game; + +public class End implements GameCommand { + + @Override + public void execute(final ChessGame chessGame) { + chessGame.gameEnd(); + } +} diff --git a/src/main/java/domain/game/GameCommand.java b/src/main/java/domain/game/GameCommand.java index 33fe738d19e..80c0ac6f9a2 100644 --- a/src/main/java/domain/game/GameCommand.java +++ b/src/main/java/domain/game/GameCommand.java @@ -1,13 +1,5 @@ package domain.game; -public enum GameCommand { - START, END, MOVE; - - public boolean isStartCommand() { - return this == START; - } - - public boolean isMoveCommand() { - return this == MOVE; - } +public interface GameCommand { + void execute(ChessGame chessGame); } diff --git a/src/main/java/domain/game/GameCommandType.java b/src/main/java/domain/game/GameCommandType.java new file mode 100644 index 00000000000..f11cab99098 --- /dev/null +++ b/src/main/java/domain/game/GameCommandType.java @@ -0,0 +1,48 @@ +package domain.game; + +import domain.board.Position; + +import java.util.Arrays; +import java.util.function.Function; + +public enum GameCommandType { + START("start", command -> new Start()), + MOVE("move", GameCommandType::toMove), + END("end", command -> new End()); + + private static final int COMMAND_INDEX = 0; + private static final int BEFORE_POSITION = 1; + private static final int AFTER_POSITION = 2; + private static final int COMMAND_WITH_POSITIONS_SIZE = 3; + + private final String value; + private final Function gameCommand; + + GameCommandType(final String value, final Function gameCommand) { + this.value = value; + this.gameCommand = gameCommand; + } + + public static GameCommand of(final String[] values) { + return Arrays.stream(GameCommandType.values()) + .filter(it -> it.value.equalsIgnoreCase(values[COMMAND_INDEX])) + .map(it -> it.gameCommand.apply(values)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 명령어입니다!")); + } + + private static Move toMove(final String[] values) { + validatePositionValue(values); + + Position source = Position.of(values[BEFORE_POSITION]); + Position destination = Position.of(values[AFTER_POSITION]); + + return new Move(source, destination); + } + + private static void validatePositionValue(final String[] values) { + if (values.length != COMMAND_WITH_POSITIONS_SIZE) { + throw new IllegalArgumentException("잘못된 출발지/도착지입니다."); + } + } +} diff --git a/src/main/java/domain/game/Move.java b/src/main/java/domain/game/Move.java new file mode 100644 index 00000000000..754477df975 --- /dev/null +++ b/src/main/java/domain/game/Move.java @@ -0,0 +1,22 @@ +package domain.game; + +import domain.board.Position; + +public class Move implements GameCommand { + private final Position source; + private final Position destination; + + public Move(final Position source, final Position destination) { + this.source = source; + this.destination = destination; + } + + @Override + public void execute(final ChessGame chessGame) { + if (!chessGame.isGameRunning()) { + throw new IllegalArgumentException("아직 게임이 시작되지 않았습니다."); + } + + chessGame.movePiece(source, destination); + } +} diff --git a/src/main/java/domain/game/Start.java b/src/main/java/domain/game/Start.java new file mode 100644 index 00000000000..c895ce4142c --- /dev/null +++ b/src/main/java/domain/game/Start.java @@ -0,0 +1,13 @@ +package domain.game; + +public class Start implements GameCommand { + + @Override + public void execute(final ChessGame chessGame) { + if (chessGame.isGameRunning()) { + throw new IllegalArgumentException("이미 게임이 진행중입니다."); + } + + chessGame.gameStart(); + } +} diff --git a/src/main/java/dto/MoveRequestDto.java b/src/main/java/dto/MoveRequestDto.java deleted file mode 100644 index e4afba7cdc6..00000000000 --- a/src/main/java/dto/MoveRequestDto.java +++ /dev/null @@ -1,35 +0,0 @@ -package dto; - -import domain.board.File; -import domain.board.Position; -import domain.board.Rank; -import domain.game.GameCommand; - -public record MoveRequestDto(GameCommand gameCommand, PositionDto source, PositionDto destination) { - - public static MoveRequestDto of(final GameCommand gameCommand, final PositionDto source, final PositionDto destination) { - return new MoveRequestDto(gameCommand, source, destination); - } - - public static MoveRequestDto of(final GameCommand gameCommand) { - return new MoveRequestDto(gameCommand, PositionDto.emptyPosition(), PositionDto.emptyPosition()); - } - - public boolean isEndCommand() { - return !gameCommand.isMoveCommand(); - } - - public Position sourcePosition() { - File file = File.of(source.row()); - Rank rank = Rank.of(source.column()); - - return new Position(file, rank); - } - - public Position destinationPosition() { - File file = File.of(destination.row()); - Rank rank = Rank.of(destination.column()); - - return new Position(file, rank); - } -} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 7592ee3fb0b..5ff300cb63c 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,115 +1,20 @@ package view; -import domain.game.GameCommand; -import dto.MoveRequestDto; -import dto.PositionDto; - -import java.util.Map; import java.util.Scanner; public class InputView { - private static final String START_COMMAND = "start"; - private static final String MOVE_COMMAND = "move"; - private static final String END_COMMAND = "end"; - private static final Map gameCommands = Map.of( - START_COMMAND, GameCommand.START, - MOVE_COMMAND, GameCommand.MOVE, - END_COMMAND, GameCommand.END - ); - private static final Map fileIndexes = Map.ofEntries( - Map.entry("a", 0), - Map.entry("b", 1), - Map.entry("c", 2), - Map.entry("d", 3), - Map.entry("e", 4), - Map.entry("f", 5), - Map.entry("g", 6), - Map.entry("h", 7), - Map.entry("8", 0), - Map.entry("7", 1), - Map.entry("6", 2), - Map.entry("5", 3), - Map.entry("4", 4), - Map.entry("3", 5), - Map.entry("2", 6), - Map.entry("1", 7) - ); - private final Scanner sc = new Scanner(System.in); - public GameCommand inputGameStartCommand() { + public String inputCommand() { String input = sc.nextLine(); - validateInputStringEmpty(input); - validateInputCommand(input); + validateEmpty(input); - return gameCommands.get(input); + return input; } - private void validateInputStringEmpty(final String input) { + private void validateEmpty(final String input) { if (input == null || input.isBlank()) { throw new IllegalArgumentException("공백을 입력할 수 없습니다."); } - - if (input.equals(MOVE_COMMAND)) { - throw new IllegalArgumentException("START 혹은 END 명령만 입력할 수 있습니다."); - } - } - - private void validateInputCommand(final String input) { - if (!gameCommands.containsKey(input)) { - throw new IllegalArgumentException("존재하지 않는 명령입니다."); - } - } - - public MoveRequestDto inputMoveRequest() { - String input = sc.nextLine(); - validateInputStringEmpty(input); - - String[] inputStrings = input.split(" "); - validateInputCommand(inputStrings); - - GameCommand gameCommand = gameCommands.get(inputStrings[0]); - if (!gameCommand.isMoveCommand()) { - return MoveRequestDto.of(gameCommand); - } - - PositionDto source = convertInputToPosition(inputStrings[1]); - PositionDto destination = convertInputToPosition(inputStrings[2]); - - return new MoveRequestDto(gameCommand, source, destination); - } - - private void validateInputCommand(final String[] input) { - if (input.length != 1 && input.length != 3) { - throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); - } - - if (!gameCommands.containsKey(input[0])) { - throw new IllegalArgumentException("존재하지 않는 명령입니다."); - } - - if (input[0].equals(START_COMMAND)) { - throw new IllegalArgumentException("MOVE 혹은 END 명령만 입력할 수 있습니다."); - } - } - - private PositionDto convertInputToPosition(final String input) { - if (input.length() != 2) { - throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); - } - - String[] inputStrings = input.split(""); - int rowIndex = parseIndex(inputStrings[0]); - int columnIndex = parseIndex(inputStrings[1]); - - return new PositionDto(rowIndex, columnIndex); - } - - private int parseIndex(final String input) { - if (!fileIndexes.containsKey(input.toLowerCase())) { - throw new IllegalArgumentException("잘못된 입력입니다. 올바른 형식으로 입력해주세요. ex) move b2 b3"); - } - - return fileIndexes.get(input.toLowerCase()); } } diff --git a/src/main/java/view/PositionConvertor.java b/src/main/java/view/PositionConvertor.java deleted file mode 100644 index c9ba2f2e484..00000000000 --- a/src/main/java/view/PositionConvertor.java +++ /dev/null @@ -1,38 +0,0 @@ -package view; - -import domain.board.File; -import domain.board.Position; -import domain.board.Rank; - -import java.util.Map; - -import static domain.board.File.*; -import static domain.board.Rank.*; - -public class PositionConvertor { - private static final Map files = Map.of( - "a", A, - "b", B, - "c", C, - "d", D, - "e", E, - "f", F, - "g", G, - "h", H - ); - private static final Map ranks = Map.of( - "1", ONE, - "2", TWO, - "3", THREE, - "4", FOUR, - "5", FIVE, - "6", SIX, - "7", SEVEN, - "8", EIGHT - ); - - public static Position convertPosition(String positionString) { - String[] split = positionString.split(""); - return new Position(files.get(split[0]), ranks.get(split[1])); - } -} From b2def81264ebf5f2b9e6ad1f086992c4eb0718e0 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 02:39:50 +0900 Subject: [PATCH 49/54] =?UTF-8?q?other:=20=EC=83=81=ED=83=9C=EC=97=90=20Co?= =?UTF-8?q?ntroller=EB=A5=BC=20=EB=84=98=EA=B8=B0=EB=8A=94=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=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/othercase/End.java | 9 +++ src/main/java/othercase/GameCommand.java | 5 ++ src/main/java/othercase/GameCommandType.java | 48 ++++++++++++ src/main/java/othercase/GameController.java | 80 ++++++++++++++++++++ src/main/java/othercase/Move.java | 18 +++++ src/main/java/othercase/Start.java | 9 +++ 6 files changed, 169 insertions(+) create mode 100644 src/main/java/othercase/End.java create mode 100644 src/main/java/othercase/GameCommand.java create mode 100644 src/main/java/othercase/GameCommandType.java create mode 100644 src/main/java/othercase/GameController.java create mode 100644 src/main/java/othercase/Move.java create mode 100644 src/main/java/othercase/Start.java diff --git a/src/main/java/othercase/End.java b/src/main/java/othercase/End.java new file mode 100644 index 00000000000..b4e6cb85eaa --- /dev/null +++ b/src/main/java/othercase/End.java @@ -0,0 +1,9 @@ +package othercase; + +public class End implements GameCommand { + + @Override + public void execute(final GameController gameController) { + gameController.endGame(); + } +} diff --git a/src/main/java/othercase/GameCommand.java b/src/main/java/othercase/GameCommand.java new file mode 100644 index 00000000000..3572688467c --- /dev/null +++ b/src/main/java/othercase/GameCommand.java @@ -0,0 +1,5 @@ +package othercase; + +public interface GameCommand { + void execute(GameController gameController); +} diff --git a/src/main/java/othercase/GameCommandType.java b/src/main/java/othercase/GameCommandType.java new file mode 100644 index 00000000000..3a0fef8b272 --- /dev/null +++ b/src/main/java/othercase/GameCommandType.java @@ -0,0 +1,48 @@ +package othercase; + +import domain.board.Position; + +import java.util.Arrays; +import java.util.function.Function; + +public enum GameCommandType { + START("start", command -> new Start()), + MOVE("move", GameCommandType::toMove), + END("end", command -> new End()); + + private static final int COMMAND_INDEX = 0; + private static final int BEFORE_POSITION = 1; + private static final int AFTER_POSITION = 2; + private static final int COMMAND_WITH_POSITIONS_SIZE = 3; + + private final String value; + private final Function gameCommand; + + GameCommandType(final String value, final Function gameCommand) { + this.value = value; + this.gameCommand = gameCommand; + } + + public static GameCommand of(final String[] values) { + return Arrays.stream(GameCommandType.values()) + .filter(it -> it.value.equalsIgnoreCase(values[COMMAND_INDEX])) + .map(it -> it.gameCommand.apply(values)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 명령어입니다!")); + } + + private static Move toMove(final String[] values) { + validatePositionValue(values); + + Position source = Position.of(values[BEFORE_POSITION]); + Position destination = Position.of(values[AFTER_POSITION]); + + return new Move(source, destination); + } + + private static void validatePositionValue(final String[] values) { + if (values.length != COMMAND_WITH_POSITIONS_SIZE) { + throw new IllegalArgumentException("잘못된 출발지/도착지입니다."); + } + } +} diff --git a/src/main/java/othercase/GameController.java b/src/main/java/othercase/GameController.java new file mode 100644 index 00000000000..3fe6d1070d9 --- /dev/null +++ b/src/main/java/othercase/GameController.java @@ -0,0 +1,80 @@ +package othercase; + +import domain.board.Position; +import domain.game.ChessGame; +import dto.BoardDto; +import view.InputView; +import view.OutputView; + +public class GameController { + private final InputView inputView; + private final OutputView outputView; + private final ChessGame chessGame; + + public GameController(final InputView inputView, final OutputView outputView, final ChessGame chessGame) { + this.inputView = inputView; + this.outputView = outputView; + this.chessGame = chessGame; + } + + public void run() { + try { + GameCommand gameCommand = inputCommand(); + gameCommand.execute(this); + } catch (Exception e) { + outputView.printErrorMessage(e.getMessage()); + run(); + } + } + + private GameCommand inputCommand() { + try { + String[] inputValues = inputView.inputCommand().split(" "); + return GameCommandType.of(inputValues); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + return inputCommand(); + } + } + + public void gameStart() { + if (isGameRunning()) { + throw new IllegalArgumentException("이미 게임이 진행중입니다."); + } + + chessGame.gameStart(); + outputView.printWelcomeMessage(); + + while (chessGame.isGameRunning()) { + BoardDto boardDto = BoardDto.from(chessGame.piecePositions()); + outputView.printBoard(boardDto); + playTurn(); + } + } + + private boolean isGameRunning() { + return chessGame.isGameRunning(); + } + + private void playTurn() { + try { + GameCommand gameCommand = inputCommand(); + gameCommand.execute(this); + } catch (Exception e) { + outputView.printErrorMessage(e.getMessage()); + playTurn(); + } + } + + public void movePiece(final Position source, final Position destination) { + if (!chessGame.isGameRunning()) { + throw new IllegalArgumentException("게임이 시작되지 않았습니다."); + } + + chessGame.movePiece(source, destination); + } + + public void endGame() { + chessGame.gameEnd(); + } +} diff --git a/src/main/java/othercase/Move.java b/src/main/java/othercase/Move.java new file mode 100644 index 00000000000..44c565bf99d --- /dev/null +++ b/src/main/java/othercase/Move.java @@ -0,0 +1,18 @@ +package othercase; + +import domain.board.Position; + +public class Move implements GameCommand { + private final Position source; + private final Position destination; + + public Move(final Position source, final Position destination) { + this.source = source; + this.destination = destination; + } + + @Override + public void execute(final GameController gameController) { + gameController.movePiece(source, destination); + } +} diff --git a/src/main/java/othercase/Start.java b/src/main/java/othercase/Start.java new file mode 100644 index 00000000000..47de1c23a48 --- /dev/null +++ b/src/main/java/othercase/Start.java @@ -0,0 +1,9 @@ +package othercase; + +public class Start implements GameCommand { + + @Override + public void execute(final GameController gameController) { + gameController.gameStart(); + } +} From ccadc3128acf3a75c380ff8d9bc81961f13c5a8e Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 02:54:07 +0900 Subject: [PATCH 50/54] =?UTF-8?q?refactor:=20import=20=EC=99=80=EC=9D=BC?= =?UTF-8?q?=EB=93=9C=20=EC=B9=B4=EB=93=9C=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/domain/board/BoardInitializer.java | 14 ++++++++++++-- src/main/java/domain/piece/Bishop.java | 6 +++++- .../java/domain/piece/PawnMovementDirection.java | 4 +++- src/main/java/domain/piece/Queen.java | 2 +- src/main/java/domain/piece/Rook.java | 9 +++++++-- src/main/java/view/OutputView.java | 6 +++++- 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/main/java/domain/board/BoardInitializer.java b/src/main/java/domain/board/BoardInitializer.java index bfdcab2cb02..093832ef77c 100644 --- a/src/main/java/domain/board/BoardInitializer.java +++ b/src/main/java/domain/board/BoardInitializer.java @@ -5,8 +5,18 @@ import java.util.HashMap; import java.util.Map; -import static domain.board.File.*; -import static domain.board.Rank.*; +import static domain.board.File.D; +import static domain.board.File.E; +import static domain.board.File.A; +import static domain.board.File.B; +import static domain.board.File.C; +import static domain.board.File.F; +import static domain.board.File.G; +import static domain.board.File.H; +import static domain.board.Rank.ONE; +import static domain.board.Rank.TWO; +import static domain.board.Rank.SEVEN; +import static domain.board.Rank.EIGHT; import static domain.piece.PieceColor.BLACK; import static domain.piece.PieceColor.WHITE; diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index e13381d5834..9245f8ed40e 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -6,8 +6,12 @@ import java.util.Map; import java.util.stream.Stream; +import static domain.piece.CommonMovementDirection.UP_RIGHT; +import static domain.piece.CommonMovementDirection.UP_LEFT; +import static domain.piece.CommonMovementDirection.DOWN_RIGHT; +import static domain.piece.CommonMovementDirection.DOWN_LEFT; +import static domain.piece.CommonMovementDirection.calculateDirection; import static domain.piece.PieceType.BISHOP; -import static domain.piece.CommonMovementDirection.*; public class Bishop extends Piece { private static final List MOVABLE_DIRECTIONS = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); diff --git a/src/main/java/domain/piece/PawnMovementDirection.java b/src/main/java/domain/piece/PawnMovementDirection.java index ac3fb41e73b..aaa4d7825ed 100644 --- a/src/main/java/domain/piece/PawnMovementDirection.java +++ b/src/main/java/domain/piece/PawnMovementDirection.java @@ -4,7 +4,9 @@ import java.util.Arrays; -import static domain.piece.PieceColor.*; +import static domain.piece.PieceColor.BLACK; +import static domain.piece.PieceColor.WHITE; + public enum PawnMovementDirection implements MovementDirection { UP_ONE_STEP(-1, 0, WHITE), diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index 0f939c520e9..c23d91bb858 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.stream.Stream; +import static domain.piece.CommonMovementDirection.calculateDirection; import static domain.piece.PieceType.QUEEN; -import static domain.piece.CommonMovementDirection.*; public class Queen extends Piece { private static final PieceType PIECE_TYPE = QUEEN; diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index d31611b91ff..171f8fa0266 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -6,8 +6,13 @@ import java.util.Map; import java.util.stream.Stream; -import static domain.piece.PieceType.*; -import static domain.piece.CommonMovementDirection.*; +import static domain.piece.CommonMovementDirection.UP; +import static domain.piece.CommonMovementDirection.DOWN; +import static domain.piece.CommonMovementDirection.RIGHT; +import static domain.piece.CommonMovementDirection.LEFT; +import static domain.piece.CommonMovementDirection.calculateDirection; +import static domain.piece.PieceType.ROOK; + public class Rook extends Piece { private static final List MOVABLE_DIRECTIONS = List.of(UP, DOWN, RIGHT, LEFT); diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 0860a5ff55d..5d3c3ab8436 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -10,8 +10,12 @@ import java.util.List; import java.util.Map; -import static domain.piece.PieceType.*; import static domain.piece.PieceType.PAWN; +import static domain.piece.PieceType.ROOK; +import static domain.piece.PieceType.KNIGHT; +import static domain.piece.PieceType.BISHOP; +import static domain.piece.PieceType.QUEEN; +import static domain.piece.PieceType.KING; public class OutputView { private static final Map pieceFormat = Map.ofEntries( From ae3d5fb44549a20ae9ec673ed36ef9c377071beb Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 03:02:13 +0900 Subject: [PATCH 51/54] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/board/Board.java | 2 +- src/main/java/domain/piece/Bishop.java | 21 +++++-------------- src/main/java/domain/piece/King.java | 2 +- src/main/java/domain/piece/Knight.java | 3 +-- src/main/java/domain/piece/Pawn.java | 2 +- .../domain/piece/PawnMovementDirection.java | 1 - src/main/java/domain/piece/Piece.java | 2 +- src/main/java/domain/piece/Queen.java | 17 +++------------ src/main/java/domain/piece/Rook.java | 21 +++++-------------- src/test/java/domain/piece/BishopTest.java | 8 +++---- src/test/java/domain/piece/KingTest.java | 6 +++--- src/test/java/domain/piece/KnightTest.java | 2 +- src/test/java/domain/piece/PawnTest.java | 8 +++---- src/test/java/domain/piece/QueenTest.java | 6 +++--- src/test/java/domain/piece/RookTest.java | 8 +++---- 15 files changed, 37 insertions(+), 72 deletions(-) diff --git a/src/main/java/domain/board/Board.java b/src/main/java/domain/board/Board.java index f67e07aa0eb..88fc0600ad2 100644 --- a/src/main/java/domain/board/Board.java +++ b/src/main/java/domain/board/Board.java @@ -18,7 +18,7 @@ public void movePiece(final PieceColor pieceColor, final Position source, final validatePosition(pieceColor, source, destination); Piece targetPiece = piecePositions.get(source); - targetPiece.checkMovable(source, destination, parsePiecePositionsIgnoreTargetPiece(source)); + targetPiece.move(source, destination, parsePiecePositionsIgnoreTargetPiece(source)); piecePositions.put(destination, targetPiece); piecePositions.remove(source); diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index 9245f8ed40e..9a3d3e13e21 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -22,12 +22,15 @@ public Bishop(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { CommonMovementDirection movementDirection = calculateDirection(source, destination); validateMovementDirection(movementDirection); - Position alivePosition = move(source, destination, movementDirection, piecePositions); + List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) + .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .toList(); + Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); checkAlivePosition(alivePosition, piecePositions); } @@ -37,20 +40,6 @@ private void validateMovementDirection(final CommonMovementDirection movementDir } } - private Position move( - final Position source, - final Position destination, - final CommonMovementDirection movementDirection, - final Map piecePositions - ) { - List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) - .takeWhile(current -> isContinuable(current, destination, piecePositions)) - .toList(); - - return movePaths.get(movePaths.size() - 1) - .next(movementDirection); - } - private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { if (current.equals(destination)) { return false; diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java index 760ee2f5e03..4a7bc441278 100644 --- a/src/main/java/domain/piece/King.java +++ b/src/main/java/domain/piece/King.java @@ -15,7 +15,7 @@ public King(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { CommonMovementDirection movementDirection = calculateDirection(source, destination); checkMoveDistance(source, destination, movementDirection); diff --git a/src/main/java/domain/piece/Knight.java b/src/main/java/domain/piece/Knight.java index fc9405c47cb..1ccd61bfa1b 100644 --- a/src/main/java/domain/piece/Knight.java +++ b/src/main/java/domain/piece/Knight.java @@ -15,11 +15,10 @@ public Knight(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { KnightMovementDirection movementDirection = calculateDirection(source, destination); Position alivePosition = source.next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); } diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java index 0aa1a1d8867..6f2365b55c7 100644 --- a/src/main/java/domain/piece/Pawn.java +++ b/src/main/java/domain/piece/Pawn.java @@ -19,7 +19,7 @@ public Pawn(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { PawnMovementDirection movementDirection = calculateDirection(color, source, destination); if (movementDirection.isCrossStep()) { checkAlivePositionOfCrossStep(source.next(movementDirection), piecePositions); diff --git a/src/main/java/domain/piece/PawnMovementDirection.java b/src/main/java/domain/piece/PawnMovementDirection.java index aaa4d7825ed..d539720861c 100644 --- a/src/main/java/domain/piece/PawnMovementDirection.java +++ b/src/main/java/domain/piece/PawnMovementDirection.java @@ -7,7 +7,6 @@ import static domain.piece.PieceColor.BLACK; import static domain.piece.PieceColor.WHITE; - public enum PawnMovementDirection implements MovementDirection { UP_ONE_STEP(-1, 0, WHITE), UP_TWO_STEP(-2, 0, WHITE), diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index 70d377e2601..859fb0242e9 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -11,7 +11,7 @@ public Piece(final PieceColor color) { this.color = color; } - public abstract void checkMovable(final Position source, final Position destination, final Map piecePositions); + public abstract void move(final Position source, final Position destination, final Map piecePositions); public abstract PieceType pieceType(); diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index c23d91bb858..b007606e8e7 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -17,26 +17,15 @@ public Queen(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { CommonMovementDirection movementDirection = calculateDirection(source, destination); - Position alivePosition = move(source, destination, movementDirection, piecePositions); - - checkAlivePosition(alivePosition, piecePositions); - } - - private Position move( - final Position source, - final Position destination, - final CommonMovementDirection movementDirection, - final Map piecePositions - ) { List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) .takeWhile(current -> isContinuable(current, destination, piecePositions)) .toList(); - return movePaths.get(movePaths.size() - 1) - .next(movementDirection); + Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); + checkAlivePosition(alivePosition, piecePositions); } private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index 171f8fa0266..1612675fe42 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -23,12 +23,15 @@ public Rook(final PieceColor color) { } @Override - public void checkMovable(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Map piecePositions) { CommonMovementDirection movementDirection = calculateDirection(source, destination); validateMovementDirection(movementDirection); - Position alivePosition = move(source, destination, movementDirection, piecePositions); + List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) + .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .toList(); + Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); checkAlivePosition(alivePosition, piecePositions); } @@ -38,20 +41,6 @@ private void validateMovementDirection(final CommonMovementDirection movementDir } } - private Position move( - final Position source, - final Position destination, - final CommonMovementDirection movementDirection, - final Map piecePositions - ) { - List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) - .takeWhile(current -> isContinuable(current, destination, piecePositions)) - .toList(); - - return movePaths.get(movePaths.size() - 1) - .next(movementDirection); - } - private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { if (current.equals(destination)) { return false; diff --git a/src/test/java/domain/piece/BishopTest.java b/src/test/java/domain/piece/BishopTest.java index 84a7514c12c..9008c1306e6 100644 --- a/src/test/java/domain/piece/BishopTest.java +++ b/src/test/java/domain/piece/BishopTest.java @@ -27,7 +27,7 @@ void checkMovableTest(final Position source, final Position destination) { Map piecePositions = Map.of(position(File.C, Rank.ONE), new Rook(PieceColor.BLACK)); // When & Then - assertThatCode(() -> bishop.checkMovable(source, destination, piecePositions)) + assertThatCode(() -> bishop.move(source, destination, piecePositions)) .doesNotThrowAnyException(); } @@ -49,7 +49,7 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio Map piecePositions = Collections.emptyMap(); // When & Then - assertThatThrownBy(() ->bishop.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() ->bishop.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); } @@ -72,7 +72,7 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d ); // When & Then - assertThatThrownBy(() -> bishop.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> bishop.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -94,7 +94,7 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Map piecePositions = Map.of(destination, new Bishop(PieceColor.WHITE)); // When & Then - assertThatThrownBy(() -> bishop.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> bishop.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/KingTest.java b/src/test/java/domain/piece/KingTest.java index 59993719e37..39b9bef89f0 100644 --- a/src/test/java/domain/piece/KingTest.java +++ b/src/test/java/domain/piece/KingTest.java @@ -27,7 +27,7 @@ void checkMovableTest(final Position source, final Position destination) { Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); // When & Then - assertThatCode(() -> king.checkMovable(source, destination, piecePositions)) + assertThatCode(() -> king.move(source, destination, piecePositions)) .doesNotThrowAnyException(); } @@ -54,7 +54,7 @@ void throwExceptionWhenDistanceOverOrEqualTwo() { Map piecePositions = Collections.emptyMap(); // When & Then - assertThatThrownBy(() -> king.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> king.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 거리입니다."); } @@ -69,7 +69,7 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); // When & Then - assertThatThrownBy(() -> king.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> king.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/KnightTest.java b/src/test/java/domain/piece/KnightTest.java index 539243b516b..ba324003a3c 100644 --- a/src/test/java/domain/piece/KnightTest.java +++ b/src/test/java/domain/piece/KnightTest.java @@ -22,7 +22,7 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); // When & Then - assertThatThrownBy(() -> knight.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> knight.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/PawnTest.java b/src/test/java/domain/piece/PawnTest.java index 78217af48f1..010bc7f33d0 100644 --- a/src/test/java/domain/piece/PawnTest.java +++ b/src/test/java/domain/piece/PawnTest.java @@ -36,7 +36,7 @@ void checkMovableTest(final PieceColor pieceColor, final PieceColor enemyColor, ); // When & Then - assertThatCode(() -> pawn.checkMovable(source, destination, piecePositions)) + assertThatCode(() -> pawn.move(source, destination, piecePositions)) .doesNotThrowAnyException(); } @@ -62,7 +62,7 @@ void throwExceptionWhenCrossStepDestinationNotHasEnemyTest(final PieceColor piec Map piecePositions = Collections.emptyMap(); // When & Then - assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("적 기물이 존재하지 않으면 대각선으로 이동할 수 없습니다"); } @@ -85,7 +85,7 @@ void throwExceptionWhenOneStepSourceIsNotStartPositionTest(final PieceColor piec Map piecePositions = Collections.emptyMap(); // When & Then - assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("시작 위치가 아니면 두 칸 이동할 수 없습니다."); } @@ -109,7 +109,7 @@ void throwExceptionWhenForwardPathHasPieceTest(final PieceColor pieceColor, fina ); // When & Then - assertThatThrownBy(() -> pawn.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("전진시 기물이 존재하는 경로 혹은 목적지로 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/QueenTest.java b/src/test/java/domain/piece/QueenTest.java index f2174f58ade..ba1154cae40 100644 --- a/src/test/java/domain/piece/QueenTest.java +++ b/src/test/java/domain/piece/QueenTest.java @@ -26,7 +26,7 @@ void checkMovableTest(final Position source, final Position destination) { Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); // When & Then - assertThatCode(() -> queen.checkMovable(source, destination, piecePositions)) + assertThatCode(() -> queen.move(source, destination, piecePositions)) .doesNotThrowAnyException(); } @@ -55,7 +55,7 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d ); // When & Then - assertThatThrownBy(() -> queen.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> queen.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -77,7 +77,7 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); // When & Then - assertThatThrownBy(() -> queen.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> queen.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/RookTest.java b/src/test/java/domain/piece/RookTest.java index 9ae4e0db1e2..fb4474b0161 100644 --- a/src/test/java/domain/piece/RookTest.java +++ b/src/test/java/domain/piece/RookTest.java @@ -27,7 +27,7 @@ void checkMovableTest(final Position source, final Position destination) { Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); // When & Then - assertThatCode(() -> rook.checkMovable(source, destination, piecePositions)) + assertThatCode(() -> rook.move(source, destination, piecePositions)) .doesNotThrowAnyException(); } @@ -49,7 +49,7 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio Map piecePositions = Collections.emptyMap(); // When & Then - assertThatThrownBy(() ->rook.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() ->rook.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); } @@ -73,7 +73,7 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d ); // When & Then - assertThatThrownBy(() -> rook.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> rook.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -95,7 +95,7 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); // When & Then - assertThatThrownBy(() -> rook.checkMovable(source, destination, piecePositions)) + assertThatThrownBy(() -> rook.move(source, destination, piecePositions)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } From 80a4ea6d48aa85eb7af71a73b8b219ca74b64f8a Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 14:30:21 +0900 Subject: [PATCH 52/54] =?UTF-8?q?refactor:=20Piece=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=8B=9C=20Board=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EB=A6=AC?= =?UTF-8?q?=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/board/Board.java | 15 ++++++++------- src/main/java/domain/piece/Bishop.java | 16 ++++++++-------- src/main/java/domain/piece/King.java | 11 +++++------ src/main/java/domain/piece/Knight.java | 11 +++++------ src/main/java/domain/piece/Pawn.java | 21 ++++++++++----------- src/main/java/domain/piece/Piece.java | 13 ++++--------- src/main/java/domain/piece/Queen.java | 16 ++++++++-------- src/main/java/domain/piece/Rook.java | 16 ++++++++-------- src/test/java/domain/piece/BishopTest.java | 13 +++++++++---- src/test/java/domain/piece/KingTest.java | 10 +++++++--- src/test/java/domain/piece/KnightTest.java | 4 +++- src/test/java/domain/piece/PawnTest.java | 13 +++++++++---- src/test/java/domain/piece/QueenTest.java | 10 +++++++--- src/test/java/domain/piece/RookTest.java | 13 +++++++++---- 14 files changed, 100 insertions(+), 82 deletions(-) diff --git a/src/main/java/domain/board/Board.java b/src/main/java/domain/board/Board.java index 88fc0600ad2..fd6885878f9 100644 --- a/src/main/java/domain/board/Board.java +++ b/src/main/java/domain/board/Board.java @@ -4,7 +4,6 @@ import domain.piece.PieceColor; import java.util.Collections; -import java.util.HashMap; import java.util.Map; public class Board { @@ -18,7 +17,7 @@ public void movePiece(final PieceColor pieceColor, final Position source, final validatePosition(pieceColor, source, destination); Piece targetPiece = piecePositions.get(source); - targetPiece.move(source, destination, parsePiecePositionsIgnoreTargetPiece(source)); + targetPiece.move(source, destination, this); piecePositions.put(destination, targetPiece); piecePositions.remove(source); @@ -33,16 +32,18 @@ private void validatePosition(final PieceColor pieceColor, final Position source throw new IllegalArgumentException("출발지에 기물이 존재하지 않습니다."); } - if (piecePositions.get(source).isEnemy(pieceColor)) { + if (!piecePositions.get(source).isTeam(pieceColor)) { throw new IllegalArgumentException("상대방의 기물을 이동시킬 수 없습니다."); } } - private Map parsePiecePositionsIgnoreTargetPiece(final Position targetPiecePosition) { - Map piecePositionsIgnoreTargetPiece = new HashMap<>(piecePositions); - piecePositionsIgnoreTargetPiece.remove(targetPiecePosition); + public boolean existPiece(final Position position) { + return piecePositions.containsKey(position); + } + + public boolean existTeamColor(final Position position, final PieceColor teamColor) { + return piecePositions.get(position).isTeam(teamColor); - return piecePositionsIgnoreTargetPiece; } public Map piecePositions() { diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index 9a3d3e13e21..4308d463a1b 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -1,9 +1,9 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import static domain.piece.CommonMovementDirection.UP_RIGHT; @@ -22,16 +22,16 @@ public Bishop(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); validateMovementDirection(movementDirection); List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) - .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .takeWhile(current -> current.equals(source) || isContinuable(current, destination, board)) .toList(); Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); + checkAlivePosition(alivePosition, board); } private void validateMovementDirection(final CommonMovementDirection movementDirection) { @@ -40,20 +40,20 @@ private void validateMovementDirection(final CommonMovementDirection movementDir } } - private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + private boolean isContinuable(final Position current, final Position destination, Board board) { if (current.equals(destination)) { return false; } - if (piecePositions.containsKey(current)) { + if (board.existPiece(current)) { throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } return true; } - private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { - if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePosition(final Position alivePosition, final Board board) { + if (board.existPiece(alivePosition) && board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java index 4a7bc441278..8bf984368ba 100644 --- a/src/main/java/domain/piece/King.java +++ b/src/main/java/domain/piece/King.java @@ -1,9 +1,8 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; -import java.util.Map; - import static domain.piece.PieceType.KING; import static domain.piece.CommonMovementDirection.calculateDirection; @@ -15,13 +14,13 @@ public King(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); checkMoveDistance(source, destination, movementDirection); Position alivePosition = source.next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); + checkAlivePosition(alivePosition, board); } private void checkMoveDistance(final Position source, final Position destination, final CommonMovementDirection movementDirection) { @@ -33,8 +32,8 @@ private void checkMoveDistance(final Position source, final Position destination } } - private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { - if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePosition(final Position alivePosition, final Board board) { + if (board.existPiece(alivePosition) && board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/Knight.java b/src/main/java/domain/piece/Knight.java index 1ccd61bfa1b..18c92df1faa 100644 --- a/src/main/java/domain/piece/Knight.java +++ b/src/main/java/domain/piece/Knight.java @@ -1,9 +1,8 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; -import java.util.Map; - import static domain.piece.PieceType.KNIGHT; import static domain.piece.KnightMovementDirection.calculateDirection; @@ -15,15 +14,15 @@ public Knight(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { KnightMovementDirection movementDirection = calculateDirection(source, destination); Position alivePosition = source.next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); + checkAlivePosition(alivePosition, board); } - private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { - if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePosition(final Position alivePosition, final Board board) { + if (board.existPiece(alivePosition) && board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java index 6f2365b55c7..31f4a61b9a0 100644 --- a/src/main/java/domain/piece/Pawn.java +++ b/src/main/java/domain/piece/Pawn.java @@ -1,9 +1,8 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; -import java.util.Map; - import static domain.board.Rank.SEVEN; import static domain.board.Rank.TWO; import static domain.piece.PieceType.PAWN; @@ -19,21 +18,21 @@ public Pawn(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { PawnMovementDirection movementDirection = calculateDirection(color, source, destination); if (movementDirection.isCrossStep()) { - checkAlivePositionOfCrossStep(source.next(movementDirection), piecePositions); + checkAlivePositionOfCrossStep(source.next(movementDirection), board); return; } if (movementDirection.isTwoStep()) { checkIsStartPosition(source); } - checkMovePaths(source, movementDirection, piecePositions); + checkMovePaths(source, movementDirection, board); } - private void checkAlivePositionOfCrossStep(final Position alivePosition, final Map piecePositions) { - if (!piecePositions.containsKey(alivePosition) || !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePositionOfCrossStep(final Position alivePosition, final Board board) { + if (!board.existPiece(alivePosition) || board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("적 기물이 존재하지 않으면 대각선으로 이동할 수 없습니다"); } } @@ -47,18 +46,18 @@ private void checkIsStartPosition(final Position source) { private void checkMovePaths( final Position source, final PawnMovementDirection movementDirection, - final Map piecePositions + final Board board ) { Position current = source; int moveDistance = Math.abs(movementDirection.getRowDistance()); for (int i = 0; i < moveDistance; i++) { current = current.next(movementDirection.convertOneStep()); - checkPathHasPiece(current, piecePositions); + checkPathHasPiece(current, board); } } - private void checkPathHasPiece(final Position path, final Map piecePositions) { - if (piecePositions.containsKey(path)) { + private void checkPathHasPiece(final Position path, final Board board) { + if (board.existPiece(path)) { throw new IllegalArgumentException("전진시 기물이 존재하는 경로 혹은 목적지로 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java index 859fb0242e9..00babe0fbc8 100644 --- a/src/main/java/domain/piece/Piece.java +++ b/src/main/java/domain/piece/Piece.java @@ -1,9 +1,8 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; -import java.util.Map; - public abstract class Piece { protected final PieceColor color; @@ -11,16 +10,12 @@ public Piece(final PieceColor color) { this.color = color; } - public abstract void move(final Position source, final Position destination, final Map piecePositions); + public abstract void move(final Position source, final Position destination, final Board board); public abstract PieceType pieceType(); - protected boolean checkEnemy(final Piece otherPiece) { - return otherPiece.isEnemy(this.color); - } - - public boolean isEnemy(final PieceColor otherColor) { - return this.color != otherColor; + public boolean isTeam(final PieceColor teamColor) { + return this.color == teamColor; } public PieceColor pieceColor() { diff --git a/src/main/java/domain/piece/Queen.java b/src/main/java/domain/piece/Queen.java index b007606e8e7..84af535703b 100644 --- a/src/main/java/domain/piece/Queen.java +++ b/src/main/java/domain/piece/Queen.java @@ -1,9 +1,9 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import static domain.piece.CommonMovementDirection.calculateDirection; @@ -17,31 +17,31 @@ public Queen(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) - .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .takeWhile(current -> current.equals(source) || isContinuable(current, destination, board)) .toList(); Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); + checkAlivePosition(alivePosition, board); } - private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + private boolean isContinuable(final Position current, final Position destination, final Board board) { if (current.equals(destination)) { return false; } - if (piecePositions.containsKey(current)) { + if (board.existPiece(current)) { throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } return true; } - private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { - if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePosition(final Position alivePosition, final Board board) { + if (board.existPiece(alivePosition) && board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } } diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index 1612675fe42..1e8b34e9812 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -1,9 +1,9 @@ package domain.piece; +import domain.board.Board; import domain.board.Position; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import static domain.piece.CommonMovementDirection.UP; @@ -23,16 +23,16 @@ public Rook(final PieceColor color) { } @Override - public void move(final Position source, final Position destination, final Map piecePositions) { + public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); validateMovementDirection(movementDirection); List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) - .takeWhile(current -> isContinuable(current, destination, piecePositions)) + .takeWhile(current -> current.equals(source) || isContinuable(current, destination, board)) .toList(); Position alivePosition = movePaths.get(movePaths.size() - 1).next(movementDirection); - checkAlivePosition(alivePosition, piecePositions); + checkAlivePosition(alivePosition, board); } private void validateMovementDirection(final CommonMovementDirection movementDirection) { @@ -41,20 +41,20 @@ private void validateMovementDirection(final CommonMovementDirection movementDir } } - private boolean isContinuable(final Position current, final Position destination, final Map piecePositions) { + private boolean isContinuable(final Position current, final Position destination, final Board board) { if (current.equals(destination)) { return false; } - if (piecePositions.containsKey(current)) { + if (board.existPiece(current)) { throw new IllegalArgumentException("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } return true; } - private void checkAlivePosition(final Position alivePosition, final Map piecePositions) { - if (piecePositions.containsKey(alivePosition) && !checkEnemy(piecePositions.get(alivePosition))) { + private void checkAlivePosition(final Position alivePosition, final Board board) { + if (board.existPiece(alivePosition) && board.existTeamColor(alivePosition, color)) { throw new IllegalArgumentException("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } } diff --git a/src/test/java/domain/piece/BishopTest.java b/src/test/java/domain/piece/BishopTest.java index 9008c1306e6..b7edd3a59de 100644 --- a/src/test/java/domain/piece/BishopTest.java +++ b/src/test/java/domain/piece/BishopTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -25,9 +26,10 @@ void checkMovableTest(final Position source, final Position destination) { // Given Bishop bishop = new Bishop(PieceColor.WHITE); Map piecePositions = Map.of(position(File.C, Rank.ONE), new Rook(PieceColor.BLACK)); + Board board = new Board(piecePositions); // When & Then - assertThatCode(() -> bishop.move(source, destination, piecePositions)) + assertThatCode(() -> bishop.move(source, destination, board)) .doesNotThrowAnyException(); } @@ -47,9 +49,10 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio // Given Bishop bishop = new Bishop(PieceColor.WHITE); Map piecePositions = Collections.emptyMap(); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() ->bishop.move(source, destination, piecePositions)) + assertThatThrownBy(() ->bishop.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); } @@ -70,9 +73,10 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d Map piecePositions = Map.of( position(File.C, Rank.THREE), new Bishop(PieceColor.BLACK) ); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> bishop.move(source, destination, piecePositions)) + assertThatThrownBy(() -> bishop.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -92,9 +96,10 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Position destination = position(File.D, Rank.FOUR); Bishop bishop = new Bishop(PieceColor.WHITE); Map piecePositions = Map.of(destination, new Bishop(PieceColor.WHITE)); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> bishop.move(source, destination, piecePositions)) + assertThatThrownBy(() -> bishop.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/KingTest.java b/src/test/java/domain/piece/KingTest.java index 39b9bef89f0..654784165c2 100644 --- a/src/test/java/domain/piece/KingTest.java +++ b/src/test/java/domain/piece/KingTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -25,9 +26,10 @@ void checkMovableTest(final Position source, final Position destination) { // Given King king = new King(PieceColor.WHITE); Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + Board board = new Board(piecePositions); // When & Then - assertThatCode(() -> king.move(source, destination, piecePositions)) + assertThatCode(() -> king.move(source, destination, board)) .doesNotThrowAnyException(); } @@ -52,9 +54,10 @@ void throwExceptionWhenDistanceOverOrEqualTwo() { Position destination = position(File.B, Rank.SIX); King king = new King(PieceColor.WHITE); Map piecePositions = Collections.emptyMap(); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> king.move(source, destination, piecePositions)) + assertThatThrownBy(() -> king.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("이동할 수 없는 거리입니다."); } @@ -67,9 +70,10 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Position destination = position(File.B, Rank.THREE); King king = new King(PieceColor.WHITE); Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> king.move(source, destination, piecePositions)) + assertThatThrownBy(() -> king.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/KnightTest.java b/src/test/java/domain/piece/KnightTest.java index ba324003a3c..0b5bd219f08 100644 --- a/src/test/java/domain/piece/KnightTest.java +++ b/src/test/java/domain/piece/KnightTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -20,9 +21,10 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Position destination = new Position(File.D, Rank.SIX); Knight knight = new Knight(PieceColor.WHITE); Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> knight.move(source, destination, piecePositions)) + assertThatThrownBy(() -> knight.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/PawnTest.java b/src/test/java/domain/piece/PawnTest.java index 010bc7f33d0..cb10e4144bc 100644 --- a/src/test/java/domain/piece/PawnTest.java +++ b/src/test/java/domain/piece/PawnTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -34,9 +35,10 @@ void checkMovableTest(final PieceColor pieceColor, final PieceColor enemyColor, position(E, FOUR), new Rook(enemyColor), position(C, FOUR), new Rook(enemyColor) ); + Board board = new Board(piecePositions); // When & Then - assertThatCode(() -> pawn.move(source, destination, piecePositions)) + assertThatCode(() -> pawn.move(source, destination, board)) .doesNotThrowAnyException(); } @@ -60,9 +62,10 @@ void throwExceptionWhenCrossStepDestinationNotHasEnemyTest(final PieceColor piec // Given Pawn pawn = new Pawn(pieceColor); Map piecePositions = Collections.emptyMap(); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("적 기물이 존재하지 않으면 대각선으로 이동할 수 없습니다"); } @@ -83,9 +86,10 @@ void throwExceptionWhenOneStepSourceIsNotStartPositionTest(final PieceColor piec // Given Pawn pawn = new Pawn(pieceColor); Map piecePositions = Collections.emptyMap(); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("시작 위치가 아니면 두 칸 이동할 수 없습니다."); } @@ -107,9 +111,10 @@ void throwExceptionWhenForwardPathHasPieceTest(final PieceColor pieceColor, fina position(D, THREE), new Rook(WHITE), position(C, THREE), new Rook(BLACK) ); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> pawn.move(source, destination, piecePositions)) + assertThatThrownBy(() -> pawn.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("전진시 기물이 존재하는 경로 혹은 목적지로 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/QueenTest.java b/src/test/java/domain/piece/QueenTest.java index ba1154cae40..2d9c3f23116 100644 --- a/src/test/java/domain/piece/QueenTest.java +++ b/src/test/java/domain/piece/QueenTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -24,9 +25,10 @@ void checkMovableTest(final Position source, final Position destination) { // Given Queen queen = new Queen(PieceColor.WHITE); Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + Board board = new Board(piecePositions); // When & Then - assertThatCode(() -> queen.move(source, destination, piecePositions)) + assertThatCode(() -> queen.move(source, destination, board)) .doesNotThrowAnyException(); } @@ -53,9 +55,10 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d position(File.B, Rank.FOUR), new Bishop(PieceColor.BLACK), position(File.D, Rank.TWO), new Rook(PieceColor.WHITE) ); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> queen.move(source, destination, piecePositions)) + assertThatThrownBy(() -> queen.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -75,9 +78,10 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Position destination = position(File.B, Rank.SIX); Queen queen = new Queen(PieceColor.WHITE); Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> queen.move(source, destination, piecePositions)) + assertThatThrownBy(() -> queen.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } diff --git a/src/test/java/domain/piece/RookTest.java b/src/test/java/domain/piece/RookTest.java index fb4474b0161..d55028210e8 100644 --- a/src/test/java/domain/piece/RookTest.java +++ b/src/test/java/domain/piece/RookTest.java @@ -1,5 +1,6 @@ package domain.piece; +import domain.board.Board; import domain.board.File; import domain.board.Position; import domain.board.Rank; @@ -25,9 +26,10 @@ void checkMovableTest(final Position source, final Position destination) { // Given Rook rook = new Rook(PieceColor.WHITE); Map piecePositions = Map.of(position(File.D, Rank.TWO), new Rook(PieceColor.BLACK)); + Board board = new Board(piecePositions); // When & Then - assertThatCode(() -> rook.move(source, destination, piecePositions)) + assertThatCode(() -> rook.move(source, destination, board)) .doesNotThrowAnyException(); } @@ -47,9 +49,10 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio // Given Rook rook = new Rook(PieceColor.WHITE); Map piecePositions = Collections.emptyMap(); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() ->rook.move(source, destination, piecePositions)) + assertThatThrownBy(() ->rook.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); } @@ -71,9 +74,10 @@ void throwExceptionWhenPathsHasPieceTest(final Position source, final Position d position(File.B, Rank.FOUR), new Rook(PieceColor.BLACK), position(File.D, Rank.TWO), new Rook(PieceColor.WHITE) ); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> rook.move(source, destination, piecePositions)) + assertThatThrownBy(() -> rook.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("목적지 경로에 기물이 존재하여 이동할 수 없습니다."); } @@ -93,9 +97,10 @@ void throwExceptionWhenDestinationHasTeamPieceTest() { Position destination = position(File.B, Rank.SIX); Rook rook = new Rook(PieceColor.WHITE); Map piecePositions = Map.of(destination, new Rook(PieceColor.WHITE)); + Board board = new Board(piecePositions); // When & Then - assertThatThrownBy(() -> rook.move(source, destination, piecePositions)) + assertThatThrownBy(() -> rook.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("아군 기물이 위치한 칸으로는 이동할 수 없습니다."); } From 6425ad6f469bffb9037dcd3d60aa5a84c0b47cb6 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 14:40:02 +0900 Subject: [PATCH 53/54] =?UTF-8?q?refactor:=20Bishop=20&=20Rook=EC=9D=98=20?= =?UTF-8?q?=EB=B0=A9=ED=96=A5=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EC=9D=84=20CommonMovementDirection=EC=97=90=20?= =?UTF-8?q?=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/piece/Bishop.java | 13 +----------- .../domain/piece/CommonMovementDirection.java | 20 +++++++++++-------- src/main/java/domain/piece/Rook.java | 13 +----------- src/test/java/domain/piece/BishopTest.java | 2 +- src/test/java/domain/piece/RookTest.java | 2 +- 5 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/main/java/domain/piece/Bishop.java b/src/main/java/domain/piece/Bishop.java index 4308d463a1b..90893491c4e 100644 --- a/src/main/java/domain/piece/Bishop.java +++ b/src/main/java/domain/piece/Bishop.java @@ -6,15 +6,10 @@ import java.util.List; import java.util.stream.Stream; -import static domain.piece.CommonMovementDirection.UP_RIGHT; -import static domain.piece.CommonMovementDirection.UP_LEFT; -import static domain.piece.CommonMovementDirection.DOWN_RIGHT; -import static domain.piece.CommonMovementDirection.DOWN_LEFT; import static domain.piece.CommonMovementDirection.calculateDirection; import static domain.piece.PieceType.BISHOP; public class Bishop extends Piece { - private static final List MOVABLE_DIRECTIONS = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); private static final PieceType PIECE_TYPE = BISHOP; public Bishop(final PieceColor color) { @@ -24,7 +19,7 @@ public Bishop(final PieceColor color) { @Override public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); - validateMovementDirection(movementDirection); + movementDirection.checkBishopMovableMovement(); List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) .takeWhile(current -> current.equals(source) || isContinuable(current, destination, board)) @@ -34,12 +29,6 @@ public void move(final Position source, final Position destination, final Board checkAlivePosition(alivePosition, board); } - private void validateMovementDirection(final CommonMovementDirection movementDirection) { - if (!MOVABLE_DIRECTIONS.contains(movementDirection)) { - throw new IllegalArgumentException("방향이 유효하지 않아 이동할 수 없는 칸입니다."); - } - } - private boolean isContinuable(final Position current, final Position destination, Board board) { if (current.equals(destination)) { return false; diff --git a/src/main/java/domain/piece/CommonMovementDirection.java b/src/main/java/domain/piece/CommonMovementDirection.java index ac9791d4faa..0ddb40fda33 100644 --- a/src/main/java/domain/piece/CommonMovementDirection.java +++ b/src/main/java/domain/piece/CommonMovementDirection.java @@ -3,7 +3,7 @@ import domain.board.Position; import java.util.Arrays; -import java.util.Set; +import java.util.List; import java.util.function.BiPredicate; public enum CommonMovementDirection implements MovementDirection { @@ -46,16 +46,20 @@ private static void validateDistance(final int rowDistance, final int columnDist } } - public static Set orthogonalVectors() { - return Set.of(UP, RIGHT, DOWN, LEFT); - } + public void checkBishopMovableMovement() { + List bishopMovableMovement = List.of(UP_RIGHT, UP_LEFT, DOWN_RIGHT, DOWN_LEFT); - public static Set diagonalVectors() { - return Set.of(UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + if (!bishopMovableMovement.contains(this)) { + throw new IllegalArgumentException("비숍이 이동할 수 있는 방향이 아닙니다."); + } } - public static Set omnidirectionalVectors() { - return Set.of(UP, RIGHT, DOWN, LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT); + public void checkRookMovableMovement() { + List rookMovableMovement = List.of(UP, DOWN, RIGHT, LEFT); + + if (!rookMovableMovement.contains(this)) { + throw new IllegalArgumentException("룩이 이동할 수 있는 방향이 아닙니다."); + } } @Override diff --git a/src/main/java/domain/piece/Rook.java b/src/main/java/domain/piece/Rook.java index 1e8b34e9812..6f5fbbf6e3a 100644 --- a/src/main/java/domain/piece/Rook.java +++ b/src/main/java/domain/piece/Rook.java @@ -6,16 +6,11 @@ import java.util.List; import java.util.stream.Stream; -import static domain.piece.CommonMovementDirection.UP; -import static domain.piece.CommonMovementDirection.DOWN; -import static domain.piece.CommonMovementDirection.RIGHT; -import static domain.piece.CommonMovementDirection.LEFT; import static domain.piece.CommonMovementDirection.calculateDirection; import static domain.piece.PieceType.ROOK; public class Rook extends Piece { - private static final List MOVABLE_DIRECTIONS = List.of(UP, DOWN, RIGHT, LEFT); private static final PieceType PIECE_TYPE = ROOK; public Rook(final PieceColor color) { @@ -25,7 +20,7 @@ public Rook(final PieceColor color) { @Override public void move(final Position source, final Position destination, final Board board) { CommonMovementDirection movementDirection = calculateDirection(source, destination); - validateMovementDirection(movementDirection); + movementDirection.checkRookMovableMovement(); List movePaths = Stream.iterate(source, current -> current.next(movementDirection)) .takeWhile(current -> current.equals(source) || isContinuable(current, destination, board)) @@ -35,12 +30,6 @@ public void move(final Position source, final Position destination, final Board checkAlivePosition(alivePosition, board); } - private void validateMovementDirection(final CommonMovementDirection movementDirection) { - if (!MOVABLE_DIRECTIONS.contains(movementDirection)) { - throw new IllegalArgumentException("방향이 유효하지 않아 이동할 수 없는 칸입니다."); - } - } - private boolean isContinuable(final Position current, final Position destination, final Board board) { if (current.equals(destination)) { return false; diff --git a/src/test/java/domain/piece/BishopTest.java b/src/test/java/domain/piece/BishopTest.java index b7edd3a59de..eaa0094bc04 100644 --- a/src/test/java/domain/piece/BishopTest.java +++ b/src/test/java/domain/piece/BishopTest.java @@ -54,7 +54,7 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio // When & Then assertThatThrownBy(() ->bishop.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + .hasMessage("비숍이 이동할 수 있는 방향이 아닙니다."); } private static Stream throwExceptionWhenInvalidDirectionTestCase() { diff --git a/src/test/java/domain/piece/RookTest.java b/src/test/java/domain/piece/RookTest.java index d55028210e8..cd934d4523b 100644 --- a/src/test/java/domain/piece/RookTest.java +++ b/src/test/java/domain/piece/RookTest.java @@ -54,7 +54,7 @@ void throwExceptionWhenInvalidDirectionTest(final Position source, final Positio // When & Then assertThatThrownBy(() ->rook.move(source, destination, board)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("방향이 유효하지 않아 이동할 수 없는 칸입니다."); + .hasMessage("룩이 이동할 수 있는 방향이 아닙니다."); } private static Stream throwExceptionWhenInvalidDirectionTestCase() { From cc8c23368a75eb1df19ca68cc31920839d4ac9d3 Mon Sep 17 00:00:00 2001 From: kelly6bf Date: Tue, 26 Mar 2024 15:13:14 +0900 Subject: [PATCH 54/54] =?UTF-8?q?refactor:=20BoardInitializer=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/domain/board/BoardInitializer.java | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/src/main/java/domain/board/BoardInitializer.java b/src/main/java/domain/board/BoardInitializer.java index 093832ef77c..967329d6e62 100644 --- a/src/main/java/domain/board/BoardInitializer.java +++ b/src/main/java/domain/board/BoardInitializer.java @@ -2,6 +2,7 @@ import domain.piece.*; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -21,43 +22,63 @@ import static domain.piece.PieceColor.WHITE; public class BoardInitializer { - private static final Map initialPiecePositions = Map.ofEntries( - Map.entry(position(E, ONE), new King(WHITE)), - Map.entry(position(D, ONE), new Queen(WHITE)), - Map.entry(position(C, ONE), new Bishop(WHITE)), - Map.entry(position(F, ONE), new Bishop(WHITE)), - Map.entry(position(B, ONE), new Knight(WHITE)), - Map.entry(position(G, ONE), new Knight(WHITE)), - Map.entry(position(A, ONE), new Rook(WHITE)), - Map.entry(position(H, ONE), new Rook(WHITE)), - Map.entry(position(A, TWO), new Pawn(WHITE)), - Map.entry(position(B, TWO), new Pawn(WHITE)), - Map.entry(position(C, TWO), new Pawn(WHITE)), - Map.entry(position(D, TWO), new Pawn(WHITE)), - Map.entry(position(E, TWO), new Pawn(WHITE)), - Map.entry(position(F, TWO), new Pawn(WHITE)), - Map.entry(position(G, TWO), new Pawn(WHITE)), - Map.entry(position(H, TWO), new Pawn(WHITE)), - Map.entry(position(E, EIGHT), new King(BLACK)), - Map.entry(position(D, EIGHT), new Queen(BLACK)), - Map.entry(position(C, EIGHT), new Bishop(BLACK)), - Map.entry(position(F, EIGHT), new Bishop(BLACK)), - Map.entry(position(B, EIGHT), new Knight(BLACK)), - Map.entry(position(G, EIGHT), new Knight(BLACK)), - Map.entry(position(A, EIGHT), new Rook(BLACK)), - Map.entry(position(H, EIGHT), new Rook(BLACK)), - Map.entry(position(A, SEVEN), new Pawn(BLACK)), - Map.entry(position(B, SEVEN), new Pawn(BLACK)), - Map.entry(position(C, SEVEN), new Pawn(BLACK)), - Map.entry(position(D, SEVEN), new Pawn(BLACK)), - Map.entry(position(E, SEVEN), new Pawn(BLACK)), - Map.entry(position(F, SEVEN), new Pawn(BLACK)), - Map.entry(position(G, SEVEN), new Pawn(BLACK)), - Map.entry(position(H, SEVEN), new Pawn(BLACK)) - ); - public static Board initBoard() { - return new Board(new HashMap<>(initialPiecePositions)); + return new Board(new HashMap<>(initPieces())); + } + + private static Map initPieces() { + final Map piecePositions = new HashMap<>(); + initKing(piecePositions); + initQueen(piecePositions); + initBishop(piecePositions); + initKnight(piecePositions); + initRook(piecePositions); + initBlackPawns(piecePositions); + initWhitePawns(piecePositions); + + return piecePositions; + } + + private static void initKing(final Map piecePositions) { + piecePositions.put(position(E, ONE), new King(WHITE)); + piecePositions.put(position(E, EIGHT), new King(BLACK)); + } + + private static void initQueen(final Map piecePositions) { + piecePositions.put(position(D, ONE), new Queen(WHITE)); + piecePositions.put(position(D, EIGHT), new Queen(BLACK)); + } + + private static void initBishop(final Map piecePositions) { + piecePositions.put(position(C, ONE), new Bishop(WHITE)); + piecePositions.put(position(F, ONE), new Bishop(WHITE)); + piecePositions.put(position(C, EIGHT), new Bishop(BLACK)); + piecePositions.put(position(F, EIGHT), new Bishop(BLACK)); + } + + private static void initKnight(final Map piecePositions) { + piecePositions.put(position(B, ONE), new Knight(WHITE)); + piecePositions.put(position(G, ONE), new Knight(WHITE)); + piecePositions.put(position(B, EIGHT), new Knight(BLACK)); + piecePositions.put(position(G, EIGHT), new Knight(BLACK)); + } + + private static void initRook(final Map piecePositions) { + piecePositions.put(position(A, ONE), new Rook(WHITE)); + piecePositions.put(position(H, ONE), new Rook(WHITE)); + piecePositions.put(position(A, EIGHT), new Rook(BLACK)); + piecePositions.put(position(H, EIGHT), new Rook(BLACK)); + + } + + private static void initWhitePawns(final Map piecePositions) { + Arrays.stream(File.values()) + .forEach(file -> piecePositions.put(position(file, TWO), new Pawn(WHITE))); + } + + private static void initBlackPawns(final Map piecePositions) { + Arrays.stream(File.values()) + .forEach(file -> piecePositions.put(position(file, SEVEN), new Pawn(BLACK))); } private static Position position(final File file, final Rank rank) {