diff --git a/README.md b/README.md index 9775dda0ae..491ba28db4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,157 @@ # java-janggi -장기 미션 저장소 +# 기능 명세서 + +--- + +## 기능 + +### 게임 초기화 + +- [x] 한나라가 위, 초나라를 아래로 위치시켜 보드를 초기화한다. + +### 게임 진행 + +- [x] 초나라 선, 한나라 후로 게임이 반복적으로 진행된다. +- [x] 입력을 받은 좌표와 기물 규칙에 따라 기물을 이동시킨다. +- [ ] 궁을 잡는 수를 두면 장군이다. +- [ ] 장군을 피하는 수를 두면 멍군이다. +- [ ] 빅장일 때 게임을 종료하기 싫으면 빅장을 깨뜨리는 수를 둘 수 있다. + +### 기물 이동 규칙 + +| **기물** | **기본 이동 방식** | **궁성(3x3) 내 특수 규칙** | **특징 (구현 시 고려사항)** | +| --- |----------------------------------| --- |----------------------------------------------------------| +| **궁(將/楚)** | 상하좌우 1칸 | 대각선 선을 따라 1칸 이동 가능 | 궁성 밖으로 절대 나갈 수 없음 | +| **사(士)** | 상하좌우 1칸 | 대각선 선을 따라 1칸 이동 가능 | 궁성 밖으로 절대 나갈 수 없음 | +| **차(車)** | 상하좌우 직선으로 거리 제한 없이 이동 | 대각선 선을 따라 이동 가능 | 경로에 다른 기물이 있으면 못 지나감 | +| **포(包)** | 상하좌우로 다른 기물 하나를 뛰어넘어 거리 제한 없이 이동 | 대각선 선을 따라 기물 하나를 넘어서 이동 | 포끼리는 서로 넘을 수 없음
포끼리는 잡을 수 없음
넘을 기물이 반드시 1개 있어야 함 | +| **마(馬)** | 상하좌우 직선 1칸 + 이동한 방향으로 대각선 1칸 | 해당 없음 | 멱(길목)이 막혀 있으면 못 감 | +| **상(象)** | 상하좌우 직선 1칸 + 이동한 방향으로 대각선 2칸 | 해당 없음 | 멱(길목)이 막혀 있으면 못 감 | +| **졸/병(卒/兵)** | 앞 또는 옆으로 1칸 | 적군 궁성 내에서 대각선 앞으로 1칸 이동 가능 | 진영에 따라 '앞'의 방향이 다름
뒤로는 못감 | + +### 게임 종료 + +- [ ] 외통수이면 승리한다. +- [ ] 빅장이면 점수를 계산해 승패를 결정한다. +- [ ] 한나라에 1.5점을 더해서 점수를 계산한다. + +## 입력 + +- [x] 초나라가 기물을 선택하고 행마를 입력한다. + + ```java + 초나라 턴입니다. 이동할 기물의 좌표와 도착할 좌표를 입력하세요. (예 : (1, 2), (3, 3)) + ``` + +- [x] 한나라가 기물을 선택하고 행마를 입력한다. + + ```java + 한나라 턴입니다. 이동할 기물의 좌표와 도착할 좌표를 입력하세요. (예 : (1, 2), (3, 3)) + ``` + + +## 출력 + +- [x] 게임 시작 멘트를 출력한다. + + ```java + 게임을 시작하겠습니다. + ``` + +- [x] 보드판을 출력한다. + + ```java + 0 1 2 3 4 5 6 7 8 + + 0 [車]-------[馬]-------[象]-------[士]-------[ ]-------[士]-------[象]-------[馬]-------[車] + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + 1 [ ]-------[ ]-------[ ]-------[ ]-------[漢]-------[ ]-------[ ]-------[ ]-------[ ] + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + 2 [ ]-------[包]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[包]-------[ ] + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + 3 [兵]-------[ ]-------[兵]-------[ ]-------[兵]-------[ ]-------[兵]-------[ ]-------[兵] + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + 4 [ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ] + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + 5 [ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ] + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + 6 [卒]-------[ ]-------[卒]-------[ ]-------[卒]-------[ ]-------[卒]-------[ ]-------[卒] + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ ㅣ + 7 [ ]-------[包]-------[ ]-------[ ]-------[ ]-------[ ]-------[ ]-------[包]-------[ ] + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ \ ㅣ / ㅣ ㅣ ㅣ ㅣ + 8 [ ]-------[ ]-------[ ]-------[ ]-------[楚]-------[ ]-------[ ]-------[ ]-------[ ] + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + ㅣ ㅣ ㅣ ㅣ / ㅣ \ ㅣ ㅣ ㅣ ㅣ + 9 [車]-------[馬]-------[象]-------[士]-------[ ]-------[士]-------[象]-------[馬]-------[車] + ``` + +- [ ] 장군이면 장군을 출력한다. + + ```java + 장군입니다! 기물을 움직여 궁을 살리세요. + ``` + +- [ ] 외통이면 게임 종료를 출력한다. + + ```java + 외통입니다. 초나라/한나라의 승리입니다. + ``` + +- [ ] 빅장이면 상대방이 선택할 수 있게 조건을 출력한다. + + ```java + 빅장입니다. 게임을 종료하기 싫으면 왕 앞에 기물을 놔두세요! + ``` + +- [ ] 빅장으로 게임이 종료되면 점수를 출력한다. + + ```java + 빅장입니다. + + 초나라 점수 : + 한나라 점수 : + + 결과 : 초나라/한나라의 승리입니다. + ``` + + +## 유효성 + +### Position + +- [x] 장기판 범위를 벗어난 경우 + ```java + [ERROR] 장기판 범위를 벗어난 좌표입니다. + ``` + +### Piece + +- [x] 기물이 이동할 수 없을 경우 + ``` + [ERROR] 유효하지 않는 행마입니다. + ``` + +### Board + +- [x] 출발지에 이동할 기물이 없는 경우 + ``` + [ERROR] 출발지에 이동할 기물이 없습니다. + ``` diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/janggi/Application.java b/src/main/java/janggi/Application.java new file mode 100644 index 0000000000..c5d0e612b3 --- /dev/null +++ b/src/main/java/janggi/Application.java @@ -0,0 +1,16 @@ +package janggi; + +import janggi.controller.Controller; +import janggi.view.InputView; +import janggi.view.OutputView; + +public class Application { + public static void main(String[] args) { + + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + Controller controller = new Controller(inputView, outputView); + + controller.run(); + } +} diff --git a/src/main/java/janggi/controller/Controller.java b/src/main/java/janggi/controller/Controller.java new file mode 100644 index 0000000000..b0f88f98ec --- /dev/null +++ b/src/main/java/janggi/controller/Controller.java @@ -0,0 +1,53 @@ +package janggi.controller; + +import janggi.domain.Board; +import janggi.domain.BoardFactory; +import janggi.domain.Column; +import janggi.domain.Position; +import janggi.domain.Row; +import janggi.domain.Team; +import janggi.dto.BoardDto; +import janggi.exception.BusinessException; +import janggi.view.InputView; +import janggi.view.OutputView; + +import java.util.List; + +public class Controller { + private final InputView inputView; + private final OutputView outputView; + + public Controller(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + outputView.printStartMessage(); + Board board = new Board(BoardFactory.generate()); + outputView.printBoard(BoardDto.from(board)); + + outputView.printStartMessage(); + + Team currentTeam = Team.CHO; + + for (int i = 0; i < 10; i++) { + while (true) { + try { + List positions = inputView.playTurn(currentTeam.getTeam()); + Position from = Position.of(Row.of(positions.get(0)), Column.of(positions.get(1))); + Position to = Position.of(Row.of(positions.get(2)), Column.of(positions.get(3))); + + board.move(from, to); + outputView.printBoard(BoardDto.from(board)); + + currentTeam = currentTeam.switchTeam(); + break; + + } catch (BusinessException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + } +} diff --git a/src/main/java/janggi/domain/Board.java b/src/main/java/janggi/domain/Board.java new file mode 100644 index 0000000000..365373604f --- /dev/null +++ b/src/main/java/janggi/domain/Board.java @@ -0,0 +1,40 @@ +package janggi.domain; + +import janggi.exception.EmptyPositionException; + +import java.util.HashMap; +import java.util.Map; + +public class Board implements BoardState{ + private final Map board; + + public Board(Map initialPieces) { + this.board = new HashMap<>(initialPieces); + } + + @Override + public boolean hasPieceAt(Position position) { + return board.containsKey(position); + } + + @Override + public Piece getPieceAt(Position position) { + return board.get(position); + } + + public void move(Position from, Position to) { + Piece movingPiece = board.get(from); + + if (movingPiece == null) { + throw new EmptyPositionException(); + } + + movingPiece.verifyMove(from, to, this); + board.remove(from); + board.put(to, movingPiece); + } + + public Map getBoard() { + return board; + } +} diff --git a/src/main/java/janggi/domain/BoardFactory.java b/src/main/java/janggi/domain/BoardFactory.java new file mode 100644 index 0000000000..df2dad5f79 --- /dev/null +++ b/src/main/java/janggi/domain/BoardFactory.java @@ -0,0 +1,106 @@ +package janggi.domain; + +import janggi.domain.movestorage.ChaMoveStorage; +import janggi.domain.movestorage.GungAndSaMoveStorage; +import janggi.domain.movestorage.JolMoveStorage; +import janggi.domain.movestorage.MaMoveStorage; +import janggi.domain.movestorage.PoMoveStorage; +import janggi.domain.movestorage.SangMoveStorage; + +import java.util.HashMap; +import java.util.Map; + +public class BoardFactory { + public static Map generate() { + Map board = new HashMap<>(); + + SetHanPieces(board); + SetChoPieces(board); + + return board; + } + + private static void SetHanPieces(Map board) { + // 차 + board.put(Position.of(Row.of(0), Column.of(0)), + new Piece(new ChaMoveStorage(), Team.HAN, 13, "車")); + board.put(Position.of(Row.of(8), Column.of(0)), + new Piece(new ChaMoveStorage(), Team.HAN, 13, "車")); + // 상 + board.put(Position.of(Row.of(1), Column.of(0)), + new Piece(new SangMoveStorage(), Team.HAN, 3, "象")); + board.put(Position.of(Row.of(6), Column.of(0)), + new Piece(new SangMoveStorage(), Team.HAN, 3, "象")); + // 마 + board.put(Position.of(Row.of(2), Column.of(0)), + new Piece(new MaMoveStorage(), Team.HAN, 5, "馬")); + board.put(Position.of(Row.of(7), Column.of(0)), + new Piece(new MaMoveStorage(), Team.HAN, 5, "馬")); + // 사 + board.put(Position.of(Row.of(3), Column.of(0)), + new Piece(new GungAndSaMoveStorage(), Team.HAN, 3, "士")); + board.put(Position.of(Row.of(5), Column.of(0)), + new Piece(new GungAndSaMoveStorage(), Team.HAN, 3, "士")); + // 궁 + board.put(Position.of(Row.of(4), Column.of(1)), + new Piece(new GungAndSaMoveStorage(), Team.HAN, 0, "漢")); + // 포 + board.put(Position.of(Row.of(1), Column.of(2)), + new Piece(new PoMoveStorage(), Team.HAN, 7, "包")); + board.put(Position.of(Row.of(7), Column.of(2)), + new Piece(new PoMoveStorage(), Team.HAN, 7, "包")); + // 졸 + board.put(Position.of(Row.of(0), Column.of(3)), + new Piece(new JolMoveStorage(), Team.HAN, 2, "兵")); + board.put(Position.of(Row.of(2), Column.of(3)), + new Piece(new JolMoveStorage(), Team.HAN, 2, "兵")); + board.put(Position.of(Row.of(4), Column.of(3)), + new Piece(new JolMoveStorage(), Team.HAN, 2, "兵")); + board.put(Position.of(Row.of(6), Column.of(3)), + new Piece(new JolMoveStorage(), Team.HAN, 2, "兵")); + board.put(Position.of(Row.of(8), Column.of(3)), + new Piece(new JolMoveStorage(), Team.HAN, 2, "兵")); + } + + private static void SetChoPieces(Map board) { + // 차 + board.put(Position.of(Row.of(0), Column.of(9)), + new Piece(new ChaMoveStorage(), Team.CHO, 13, "車")); + board.put(Position.of(Row.of(8), Column.of(9)), + new Piece(new ChaMoveStorage(), Team.CHO, 13, "車")); + // 상 + board.put(Position.of(Row.of(1), Column.of(9)), + new Piece(new SangMoveStorage(), Team.CHO, 3, "象")); + board.put(Position.of(Row.of(6), Column.of(9)), + new Piece(new SangMoveStorage(), Team.CHO, 3, "象")); + // 마 + board.put(Position.of(Row.of(2), Column.of(9)), + new Piece(new MaMoveStorage(), Team.CHO, 5, "馬")); + board.put(Position.of(Row.of(7), Column.of(9)), + new Piece(new MaMoveStorage(), Team.CHO, 5, "馬")); + // 사 + board.put(Position.of(Row.of(3), Column.of(9)), + new Piece(new GungAndSaMoveStorage(), Team.CHO, 3, "士")); + board.put(Position.of(Row.of(5), Column.of(9)), + new Piece(new GungAndSaMoveStorage(), Team.CHO, 3, "士")); + // 궁 + board.put(Position.of(Row.of(4), Column.of(8)), + new Piece(new GungAndSaMoveStorage(), Team.CHO, 0, "楚")); + // 포 + board.put(Position.of(Row.of(1), Column.of(7)), + new Piece(new PoMoveStorage(), Team.CHO, 7, "包")); + board.put(Position.of(Row.of(7), Column.of(7)), + new Piece(new PoMoveStorage(), Team.CHO, 7, "包")); + // 졸 + board.put(Position.of(Row.of(0), Column.of(6)), + new Piece(new JolMoveStorage(), Team.CHO, 2, "卒")); + board.put(Position.of(Row.of(2), Column.of(6)), + new Piece(new JolMoveStorage(), Team.CHO, 2, "卒")); + board.put(Position.of(Row.of(4), Column.of(6)), + new Piece(new JolMoveStorage(), Team.CHO, 2, "卒")); + board.put(Position.of(Row.of(6), Column.of(6)), + new Piece(new JolMoveStorage(), Team.CHO, 2, "卒")); + board.put(Position.of(Row.of(8), Column.of(6)), + new Piece(new JolMoveStorage(), Team.CHO, 2, "卒")); + } +} diff --git a/src/main/java/janggi/domain/BoardState.java b/src/main/java/janggi/domain/BoardState.java new file mode 100644 index 0000000000..ca1eb7fe5f --- /dev/null +++ b/src/main/java/janggi/domain/BoardState.java @@ -0,0 +1,6 @@ +package janggi.domain; + +public interface BoardState { //인터페이스명 (readable) + boolean hasPieceAt(Position position); + Piece getPieceAt(Position position); +} diff --git a/src/main/java/janggi/domain/Column.java b/src/main/java/janggi/domain/Column.java new file mode 100644 index 0000000000..57e58543e4 --- /dev/null +++ b/src/main/java/janggi/domain/Column.java @@ -0,0 +1,54 @@ +package janggi.domain; + +import janggi.exception.ColumnOutOfRangeException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class Column { + private static final int MIN = 0; + private static final int MAX = 9; + private static final Map CACHE = new HashMap<>(); // 직접 실험해서 성능 차이 확인 + + static { + for (int i = MIN; i <= MAX; i++) { + CACHE.put(i, new Column(i)); + } + } + + private final int value; + + private Column(int value) { + this.value = value; + } + public static Column of(int value) { + if (!CACHE.containsKey(value)) { + throw new ColumnOutOfRangeException(); + } + return CACHE.get(value); + } + + public static Collection values() { + return CACHE.values(); + } + + public int getColumn() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + Column column = (Column) o; + return this.value == column.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/janggi/domain/Piece.java b/src/main/java/janggi/domain/Piece.java new file mode 100644 index 0000000000..65f1287da3 --- /dev/null +++ b/src/main/java/janggi/domain/Piece.java @@ -0,0 +1,56 @@ +package janggi.domain; + +import janggi.domain.movestorage.MoveStorage; +import janggi.exception.InvalidMoveException; + +import java.util.Objects; + +public class Piece { + private final MoveStorage moveStorage; + private final Team team; + private final int score; + private final String name; + + public Piece(MoveStorage moveStorage, Team team, int score, String name) { + this.moveStorage = moveStorage; + this.team = team; + this.score = score; + this.name = name; + } + + public void verifyMove(Position from, Position to, BoardState boardState) { + if (!moveStorage.canMove(from, to, boardState)) { + throw new InvalidMoveException(); + } + + Piece pieceTo = boardState.getPieceAt(to); + + if (pieceTo != null && isSameTeam(pieceTo)) { + throw new InvalidMoveException(); + } + } + + private boolean isSameTeam(Piece pieceTo) { + return team == pieceTo.getTeam(); + } + + public String getName() { + return name; + } + + public Team getTeam() { + return team; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Piece piece = (Piece) o; + return score == piece.score && Objects.equals(moveStorage, piece.moveStorage) && team == piece.team && Objects.equals(name, piece.name); + } + + @Override + public int hashCode() { + return Objects.hash(moveStorage, team, score, name); + } +} diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java new file mode 100644 index 0000000000..017f97613f --- /dev/null +++ b/src/main/java/janggi/domain/Position.java @@ -0,0 +1,55 @@ +package janggi.domain; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class Position { + private static final Map CACHE = new HashMap<>(); + + static { + for (Row row : Row.values()) { + for (Column column : Column.values()) { + CACHE.put(toKey(row, column), new Position(row, column)); + } + } + } + + private final Row x; + private final Column y; + + private Position(Row x, Column y) { + this.x = x; + this.y = y; + } + + public static Position of(Row x, Column y) { + String key = toKey(x, y); + return CACHE.get(key); + } + + private static String toKey(Row x, Column y) { + return x.getRow() + "," + y.getColumn(); + } + + public List getPosition() { // Position을 활용 + List position = new ArrayList<>(); + position.add(x.getRow()); // 캡슐화 깨짐 + position.add(y.getColumn()); + return position; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Position position = (Position) o; + return Objects.equals(x, position.x) && Objects.equals(y, position.y); + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} diff --git a/src/main/java/janggi/domain/Row.java b/src/main/java/janggi/domain/Row.java new file mode 100644 index 0000000000..fc8c790360 --- /dev/null +++ b/src/main/java/janggi/domain/Row.java @@ -0,0 +1,54 @@ +package janggi.domain; + +import janggi.exception.RowOutOfRangeException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class Row { + private static final int MIN = 0; + private static final int MAX = 8; + private static final Map CACHE = new HashMap<>(); + + static { + for (int i = MIN; i <= MAX; i++) { + CACHE.put(i, new Row(i)); + } + } + + private final int value; + + private Row(int value) { + this.value = value; + } + public static Row of(int value) { + if (!CACHE.containsKey(value)) { + throw new RowOutOfRangeException(); + } + return CACHE.get(value); + } + + public static Collection values() { + return CACHE.values(); + } + + public int getRow() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + Row row = (Row) o; + return this.value == row.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/janggi/domain/Team.java b/src/main/java/janggi/domain/Team.java new file mode 100644 index 0000000000..bb288031a2 --- /dev/null +++ b/src/main/java/janggi/domain/Team.java @@ -0,0 +1,23 @@ +package janggi.domain; + +public enum Team { + CHO("초"), + HAN("한"); + + private final String name; + + Team(String name) { + this.name = name; + } + + public Team switchTeam() { + if (this == Team.CHO) { + return Team.HAN; + } + return Team.CHO; + } + + public String getTeam() { + return name; + } +} diff --git a/src/main/java/janggi/domain/movestorage/ChaMoveStorage.java b/src/main/java/janggi/domain/movestorage/ChaMoveStorage.java new file mode 100644 index 0000000000..584565738f --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/ChaMoveStorage.java @@ -0,0 +1,51 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Position; +import janggi.domain.Row; + +import java.util.List; + +public class ChaMoveStorage implements MoveStorage { + + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + if (fromX != toX && fromY != toY) { + return false; + } + + if (fromX == toX) { + int start = Math.min(fromY, toY) + 1; + int end = Math.max(fromY, toY); + + for (int i = start; i < end; i++) { + Position position = Position.of(Row.of(fromX), Column.of(i)); + if (boardState.hasPieceAt(position)) { + return false; + } + } + } + + if (fromY == toY) { + int start = Math.min(fromX, toX) + 1; + int end = Math.max(fromX, toX); + + for (int i = start; i < end; i++) { + Position position = Position.of(Row.of(i), Column.of(fromY)); + if (boardState.hasPieceAt(position)) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/janggi/domain/movestorage/GungAndSaMoveStorage.java b/src/main/java/janggi/domain/movestorage/GungAndSaMoveStorage.java new file mode 100644 index 0000000000..19bf9891a7 --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/GungAndSaMoveStorage.java @@ -0,0 +1,52 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Position; +import janggi.domain.Team; + +import java.util.List; + +public class GungAndSaMoveStorage implements MoveStorage{ + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + if (!(3 <= fromX && fromX <= 5) || !(3 <= toX && toX <= 5)) { + return false; + } + + Team currentTeam = boardState.getPieceAt(from).getTeam(); + + if (currentTeam == Team.HAN) { + if (!(0 <= fromY && fromY <= 2) || !(0 <= toY && toY <= 2)) { + return false; + } + } + + if (currentTeam == Team.CHO) { + if (!(7 <= fromY && fromY <= 9) || !(7 <= toY && toY <= 9)) { + return false; + } + } + + if (fromX != toX && fromY != toY) { + return false; + } + + if (fromY == toY && Math.abs(fromX - toX) != 1) { + return false; + } + + if (fromX == toX && Math.abs(fromY - toY) != 1) { + return false; + } + + return true; + } +} diff --git a/src/main/java/janggi/domain/movestorage/JolMoveStorage.java b/src/main/java/janggi/domain/movestorage/JolMoveStorage.java new file mode 100644 index 0000000000..fd7ecd09ac --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/JolMoveStorage.java @@ -0,0 +1,44 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Position; +import janggi.domain.Team; + +import java.util.List; + +public class JolMoveStorage implements MoveStorage{ + private static final int HAN_FORWARD = 1; + private static final int CHO_FORWARD = -1; + public static final int NEXT_TO = 1; + + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + if (Math.abs(fromX - toX) == NEXT_TO && fromY == toY) { + return true; + } + + Team currentTeam = boardState.getPieceAt(from).getTeam(); + + if (currentTeam == Team.HAN) { + if (toY - fromY == HAN_FORWARD && fromX == toX) { + return true; + } + } + + if (currentTeam == Team.CHO) { + if (toY - fromY == CHO_FORWARD && fromX == toX) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/janggi/domain/movestorage/MaMoveStorage.java b/src/main/java/janggi/domain/movestorage/MaMoveStorage.java new file mode 100644 index 0000000000..fca865b3e0 --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/MaMoveStorage.java @@ -0,0 +1,62 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Position; +import janggi.domain.Row; + +import java.util.ArrayList; +import java.util.List; + +public class MaMoveStorage implements MoveStorage{ + private static final int FORWARD = 2; + private static final int DIAGONAL = 1; + + private static final int PATH_STEP_1 = 1; + private static final int PATH_STEP_0 = 0; + + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); //Row, Col을 사용하지 않고, int를 사용하고 있음 + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + int diffX = toX - fromX; + int diffY = toY - fromY; + + boolean isMaMove = (Math.abs(diffX) == FORWARD && Math.abs(diffY) == DIAGONAL) || + (Math.abs(diffX) == DIAGONAL && Math.abs(diffY) == FORWARD); + + if (!isMaMove) { + return false; + } + + List movementPathPositions = new ArrayList<>(); + + int signX = Integer.signum(diffX); + int signY = Integer.signum(diffY); + + if (Math.abs(diffX) == FORWARD) { + int step1X = fromX + (signX * PATH_STEP_1); + int step1Y = fromY + (signY * PATH_STEP_0); + movementPathPositions.add(Position.of(Row.of(step1X), Column.of(step1Y))); + } + + if (Math.abs(diffY) == FORWARD) { + int step1X = fromX + (signX * PATH_STEP_0); + int step1Y = fromY + (signY * PATH_STEP_1); + movementPathPositions.add(Position.of(Row.of(step1X), Column.of(step1Y))); + } + + for (Position position : movementPathPositions) { + if (boardState.hasPieceAt(position)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/janggi/domain/movestorage/MoveStorage.java b/src/main/java/janggi/domain/movestorage/MoveStorage.java new file mode 100644 index 0000000000..7068b24ff6 --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/MoveStorage.java @@ -0,0 +1,8 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Position; + +public interface MoveStorage { + boolean canMove(Position from, Position to, BoardState boardState); +} diff --git a/src/main/java/janggi/domain/movestorage/PoMoveStorage.java b/src/main/java/janggi/domain/movestorage/PoMoveStorage.java new file mode 100644 index 0000000000..dcce09a47e --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/PoMoveStorage.java @@ -0,0 +1,73 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; + +import java.util.List; + +public class PoMoveStorage implements MoveStorage { + + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + if (fromX != toX && fromY != toY) { + return false; + } + + int jumpCount = 0; + + if (fromX == toX) { + int start = Math.min(fromY, toY) + 1; + int end = Math.max(fromY, toY); + + for (int i = start; i < end; i++) { + Position position = Position.of(Row.of(fromX), Column.of(i)); + if (boardState.hasPieceAt(position)) { + Piece jumpPiece = boardState.getPieceAt(position); + if (jumpPiece.getName().equals("包")) { + return false; + } + jumpCount++; + } + } + } + + if (fromY == toY) { + int start = Math.min(fromX, toX) + 1; + int end = Math.max(fromX, toX); + + for (int i = start; i < end; i++) { + Position position = Position.of(Row.of(i), Column.of(fromY)); + if (boardState.hasPieceAt(position)) { + Piece jumpPiece = boardState.getPieceAt(position); + if (jumpPiece.getName().equals("包")) { + return false; + } + jumpCount++; + } + } + } + + if (jumpCount != 1) { + return false; + } + + if (boardState.hasPieceAt(to)) { + Piece targetPiece = boardState.getPieceAt(to); + if (targetPiece.getName().equals("包")) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/janggi/domain/movestorage/SangMoveStorage.java b/src/main/java/janggi/domain/movestorage/SangMoveStorage.java new file mode 100644 index 0000000000..63454f7f96 --- /dev/null +++ b/src/main/java/janggi/domain/movestorage/SangMoveStorage.java @@ -0,0 +1,71 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Position; +import janggi.domain.Row; + +import java.util.ArrayList; +import java.util.List; + +public class SangMoveStorage implements MoveStorage{ + private static final int FORWARD = 3; + private static final int DIAGONAL = 2; + + private static final int PATH_STEP_2 = 2; + private static final int PATH_STEP_1 = 1; + private static final int PATH_STEP_0 = 0; + + @Override + public boolean canMove(Position from, Position to, BoardState boardState) { + List fromPosition = from.getPosition(); + List toPosition = to.getPosition(); + + int fromX = fromPosition.getFirst(); + int fromY = fromPosition.getLast(); + int toX = toPosition.getFirst(); + int toY = toPosition.getLast(); + + int diffX = toX - fromX; + int diffY = toY - fromY; + + boolean isSangMove = (Math.abs(diffX) == FORWARD && Math.abs(diffY) == DIAGONAL) || + (Math.abs(diffX) == DIAGONAL && Math.abs(diffY) == FORWARD); + + if (!isSangMove) { + return false; + } + + List movementPathPositions = new ArrayList<>(); + + int signX = Integer.signum(diffX); + int signY = Integer.signum(diffY); + + if (Math.abs(diffX) == FORWARD) { + int step1X = fromX + (signX * PATH_STEP_1); + int step1Y = fromY + (signY * PATH_STEP_0); + movementPathPositions.add(Position.of(Row.of(step1X), Column.of(step1Y))); + + int step2X = fromX + (signX * PATH_STEP_2); + int step2Y = fromY + (signY * PATH_STEP_1); + movementPathPositions.add(Position.of(Row.of(step2X), Column.of(step2Y))); + } + + if (Math.abs(diffY) == FORWARD) { + int step1X = fromX + (signX * PATH_STEP_0); + int step1Y = fromY + (signY * PATH_STEP_1); + movementPathPositions.add(Position.of(Row.of(step1X), Column.of(step1Y))); + + int step2X = fromX + (signX * PATH_STEP_1); + int step2Y = fromY + (signY * PATH_STEP_2); + movementPathPositions.add(Position.of(Row.of(step2X), Column.of(step2Y))); + } + + for (Position position : movementPathPositions) { + if (boardState.hasPieceAt(position)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/janggi/dto/BoardDto.java b/src/main/java/janggi/dto/BoardDto.java new file mode 100644 index 0000000000..16ea8573c4 --- /dev/null +++ b/src/main/java/janggi/dto/BoardDto.java @@ -0,0 +1,31 @@ +package janggi.dto; + +import janggi.domain.Board; +import janggi.domain.Piece; +import janggi.domain.Position; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BoardDto { + private final Map, String> pieces; + + private BoardDto(Map, String> pieces) { + this.pieces = pieces; + } + + public static BoardDto from(Board board) { + Map, String> result = new HashMap<>(); + Map pieces = board.getBoard(); + + for (Position position : pieces.keySet()) { + result.put(position.getPosition(), pieces.get(position).getName()); + } + return new BoardDto(result); + } + + public Map, String> getPieces() { + return pieces; + } +} diff --git a/src/main/java/janggi/exception/BusinessException.java b/src/main/java/janggi/exception/BusinessException.java new file mode 100644 index 0000000000..5c412dff5d --- /dev/null +++ b/src/main/java/janggi/exception/BusinessException.java @@ -0,0 +1,7 @@ +package janggi.exception; + +public class BusinessException extends RuntimeException { + public BusinessException(String message) { + super("[ERROR] " + message); + } +} diff --git a/src/main/java/janggi/exception/ColumnOutOfRangeException.java b/src/main/java/janggi/exception/ColumnOutOfRangeException.java new file mode 100644 index 0000000000..6e5449bb10 --- /dev/null +++ b/src/main/java/janggi/exception/ColumnOutOfRangeException.java @@ -0,0 +1,7 @@ +package janggi.exception; + +public class ColumnOutOfRangeException extends BusinessException { + public ColumnOutOfRangeException() { + super("장기판의 y좌표 범위를 벗어났습니다."); + } +} diff --git a/src/main/java/janggi/exception/EmptyPositionException.java b/src/main/java/janggi/exception/EmptyPositionException.java new file mode 100644 index 0000000000..bc8c38bc56 --- /dev/null +++ b/src/main/java/janggi/exception/EmptyPositionException.java @@ -0,0 +1,7 @@ +package janggi.exception; + +public class EmptyPositionException extends BusinessException { + public EmptyPositionException() { + super("출발지에 이동할 기물이 없습니다."); + } +} diff --git a/src/main/java/janggi/exception/InvalidMoveException.java b/src/main/java/janggi/exception/InvalidMoveException.java new file mode 100644 index 0000000000..e281901cb4 --- /dev/null +++ b/src/main/java/janggi/exception/InvalidMoveException.java @@ -0,0 +1,7 @@ +package janggi.exception; + +public class InvalidMoveException extends BusinessException { + public InvalidMoveException() { + super("유효하지 않는 행마입니다."); + } +} diff --git a/src/main/java/janggi/exception/RowOutOfRangeException.java b/src/main/java/janggi/exception/RowOutOfRangeException.java new file mode 100644 index 0000000000..b9a7b411f6 --- /dev/null +++ b/src/main/java/janggi/exception/RowOutOfRangeException.java @@ -0,0 +1,7 @@ +package janggi.exception; + +public class RowOutOfRangeException extends BusinessException { + public RowOutOfRangeException() { + super("장기판의 x좌표 범위를 벗어났습니다."); + } +} diff --git a/src/main/java/janggi/view/InputView.java b/src/main/java/janggi/view/InputView.java new file mode 100644 index 0000000000..3e9968c887 --- /dev/null +++ b/src/main/java/janggi/view/InputView.java @@ -0,0 +1,34 @@ +package janggi.view; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private final Scanner sc; + + public InputView() { + this.sc = new Scanner(System.in); + } + + public List playTurn(String team) { + System.out.println(team + "나라 턴입니다."); + + List inputFrom = readCoordinates("이동할 기물의 좌표를 입력하세요. (예 : 1, 2)"); + List inputTo = readCoordinates("도착할 좌표를 입력하세요. (예 : 1, 3)"); + + List result = new ArrayList<>(inputFrom); + result.addAll(inputTo); + + return result; + } + + private List readCoordinates(String message) { + System.out.println(message); + return Arrays.stream(sc.nextLine().split(",")) + .map(String::trim) + .map(Integer::parseInt) + .toList(); + } +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java new file mode 100644 index 0000000000..b13dca1ce8 --- /dev/null +++ b/src/main/java/janggi/view/OutputView.java @@ -0,0 +1,134 @@ +package janggi.view; + +import janggi.dto.BoardDto; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class OutputView { + + private static final int BOARD_WIDTH = 9; + private static final int BOARD_HEIGHT = 10; + private static final int NODE_CHARS = 10; + private static final int SPACER_LINES = 3; + private static final String EMPTY_NODE = "[ ]"; + + private static final String ANSI_RED = "\u001B[31m"; + private static final String ANSI_GREEN = "\u001B[32m"; + private static final String ANSI_RESET = "\u001B[0m"; + + public void printStartMessage() { + System.out.println("게임을 시작하겠습니다."); + System.out.println(); + } + + public void printErrorMessage(String errorMessage) { + System.out.println(errorMessage); + } + + public void printBoard(BoardDto boardDto) { + String[][] boardView = generateBoardView(boardDto); + + printXCoordinates(); + for (int y = 0; y < BOARD_HEIGHT; y++) { + printNodeRow(boardView, y); + if (y < BOARD_HEIGHT - 1) { + printSpacerRows(y); + } + } + System.out.println(); + } + + private String[][] generateBoardView(BoardDto boardDto) { + String[][] boardView = new String[BOARD_HEIGHT][BOARD_WIDTH]; + + for (int y = 0; y < BOARD_HEIGHT; y++) { + Arrays.fill(boardView[y], EMPTY_NODE); + } + + Map, String> pieces = boardDto.getPieces(); + for (Map.Entry, String> entry : pieces.entrySet()) { + List position = entry.getKey(); + int x = position.get(0); + int y = position.get(1); + String pieceName = entry.getValue(); + + String colorCode = determineFactionColor(pieceName, y); + + String formattedPiece = String.format("[%s%s%s]", colorCode, pieceName, ANSI_RESET); + boardView[y][x] = formattedPiece; + } + + return boardView; + } + + private String determineFactionColor(String pieceName, int y) { + if (pieceName.equals("漢") || pieceName.equals("兵")) { + return ANSI_RED; + } + if (pieceName.equals("楚") || pieceName.equals("卒")) { + return ANSI_GREEN; + } + + if (y <= 4) { + return ANSI_RED; + } + return ANSI_GREEN; + } + + private void printXCoordinates() { + StringBuilder xAxis = new StringBuilder(" "); + for (int x = 0; x < BOARD_WIDTH; x++) { + char fullWidthNum = (char) ('\uFF10' + x); + xAxis.append(" ").append(fullWidthNum).append(" "); + } + System.out.println(xAxis.toString()); + System.out.println(); + } + + private void printNodeRow(String[][] boardView, int y) { + StringBuilder row = new StringBuilder(); + row.append(y).append(" "); + for (int x = 0; x < BOARD_WIDTH; x++) { + row.append(boardView[y][x]); + if (x < BOARD_WIDTH - 1) { + row.append("-------"); + } + } + System.out.println(row.toString()); + } + + private void printSpacerRows(int y) { + int[] diagonalOffsets = {2, 5, 8}; + + for (int spacerIndex = 0; spacerIndex < SPACER_LINES; spacerIndex++) { + char[] spacer = new char[BOARD_WIDTH * NODE_CHARS]; + Arrays.fill(spacer, ' '); + + for (int x = 0; x < BOARD_WIDTH; x++) { + spacer[x * NODE_CHARS + 1] = 'ㅣ'; + } + + int offset = diagonalOffsets[spacerIndex]; + addPalaceDiagonals(spacer, y, offset); + + System.out.print(" "); + System.out.println(new String(spacer)); + } + } + + private void addPalaceDiagonals(char[] spacer, int y, int offset) { + int x3Center = 3 * NODE_CHARS + 1; + int x4Center = 4 * NODE_CHARS + 1; + int x5Center = 5 * NODE_CHARS + 1; + + if (y == 0 || y == 7) { + spacer[x3Center + offset] = '\\'; + spacer[x5Center - offset] = '/'; + } else if (y == 1 || y == 8) { + spacer[x4Center - offset] = '/'; + spacer[x4Center + offset] = '\\'; + } + } +} diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java new file mode 100644 index 0000000000..05c6046826 --- /dev/null +++ b/src/test/java/janggi/domain/BoardTest.java @@ -0,0 +1,68 @@ +package janggi.domain; + +import janggi.domain.movestorage.JolMoveStorage; +import janggi.exception.EmptyPositionException; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BoardTest { + @Test + void 보드는_특정_위치에_기물이_존재하는지_확인할_수_있다() { + // give + Position position = Position.of(Row.of(0), Column.of(7)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + Board board = new Board(Map.of(position, piece)); + + // when & then + assertThat(board.hasPieceAt(position)).isTrue(); + } + + @Test + void 보드는_지정된_좌표의_기물_정보를_알려준다() { + // give + Position position = Position.of(Row.of(0), Column.of(7)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + Board board = new Board(Map.of(position, piece)); + + // when & then + assertThat(board.getPieceAt(position)).isEqualTo(piece); + } + + @Test + void 기물은_허용된_규칙에_따라_새로운_위치로_이동한다() { + // give + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(1)); + + Position position = Position.of(Row.of(0), Column.of(0)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Board board = new Board(Map.of(position, piece)); + + // when + board.move(from, to); + + // then + assertThat(board.getPieceAt(to)).isEqualTo(piece); + assertThat(board.hasPieceAt(from)).isFalse(); + } + + @Test + void 기물이_존재하지_않는_곳에서는_이동을_시작할_수_없다() { + // give + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(1)); + + Position position = Position.of(Row.of(0), Column.of(7)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Board board = new Board(Map.of(position, piece)); + + // when & then + assertThatThrownBy(() -> board.move(from, to)).isInstanceOf(EmptyPositionException.class); + } +} diff --git a/src/test/java/janggi/domain/ColumnTest.java b/src/test/java/janggi/domain/ColumnTest.java new file mode 100644 index 0000000000..f59872ca4d --- /dev/null +++ b/src/test/java/janggi/domain/ColumnTest.java @@ -0,0 +1,30 @@ +package janggi.domain; + + +import janggi.exception.ColumnOutOfRangeException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class ColumnTest { + @Test + void 장기판의_y좌표_범위를_벗어나면_예외가_발생한다() { + Assertions.assertThatThrownBy(() -> Column.of(10)).isInstanceOf(ColumnOutOfRangeException.class); + Assertions.assertThatThrownBy(() -> Column.of(-1)).isInstanceOf(ColumnOutOfRangeException.class); + } + + @Test + void 장기판의_y좌표_범위_내라면_예외가_발생하지_않는다() { + Column column1 = Column.of(0); + Column column2 = Column.of(9); + + Assertions.assertThat(column1.getColumn()).isEqualTo(0); + Assertions.assertThat(column2.getColumn()).isEqualTo(9); + } + + @Test + void 동등성_테스트() { + Column column = Column.of(1); + + Assertions.assertThat(column).isEqualTo(Column.of(1)); + } +} diff --git a/src/test/java/janggi/domain/PieceTest.java b/src/test/java/janggi/domain/PieceTest.java new file mode 100644 index 0000000000..63ca45c852 --- /dev/null +++ b/src/test/java/janggi/domain/PieceTest.java @@ -0,0 +1,73 @@ +package janggi.domain; + +import janggi.domain.movestorage.JolMoveStorage; +import janggi.domain.movestorage.MoveStorage; +import janggi.exception.InvalidMoveException; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PieceTest { + private static class ObstaclFakeBoard implements BoardState { + private final Map obstacles; + + public ObstaclFakeBoard(Map obstacles) { + this.obstacles = new HashMap<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.containsKey(position); + } + + @Override + public Piece getPieceAt(Position position) { + return obstacles.get(position); + } + } + + @Test + void 다른_진영의_기물을_잡았을_경우_예외가_발생하지_않는다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(4), Column.of(4)); + + Piece pieceHan = new Piece(new JolMoveStorage(), Team.HAN, 9, "兵"); + Piece pieceCho = new Piece(new JolMoveStorage(), Team.CHO, 9, "卒"); + + Map fakeBoard = new HashMap<>(); + fakeBoard.put(from, pieceHan); + fakeBoard.put(to, pieceCho); + + BoardState boardState = new ObstaclFakeBoard(fakeBoard); + + // when & then + assertThatCode(() -> pieceHan.verifyMove(from, to, boardState)) + .doesNotThrowAnyException(); + } + + @Test + void 같은_진영의_기물을_잡았을_경우_예외가_발생한다() { + // given + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(4), Column.of(4)); + + Piece pieceHan1 = new Piece(new JolMoveStorage(), Team.HAN, 2, "兵"); + Piece pieceHan2 = new Piece(new JolMoveStorage(), Team.HAN, 2, "兵"); + + Map fakeBoard = new HashMap<>(); + fakeBoard.put(from, pieceHan1); + fakeBoard.put(to, pieceHan2); + + BoardState boardState = new ObstaclFakeBoard(fakeBoard); + + // when & then + assertThatThrownBy(() -> pieceHan1.verifyMove(from, to, boardState)) + .isInstanceOf(InvalidMoveException.class); + } +} diff --git a/src/test/java/janggi/domain/PositionTest.java b/src/test/java/janggi/domain/PositionTest.java new file mode 100644 index 0000000000..bf56f7a987 --- /dev/null +++ b/src/test/java/janggi/domain/PositionTest.java @@ -0,0 +1,24 @@ +package janggi.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +class PositionTest { + + @Test + void 장기판에_모든_좌표가_생성된다() { + // when & then + for (int x = 0; x < 9; x++) { + for(int y = 0; y < 10; y++) { + Position position1 = Position.of(Row.of(x), Column.of(y)); + Position position2 = Position.of(Row.of(x), Column.of(y)); + + assertThat(position1) + .as("x=%d, y=%d 에서 캐싱 실패", x, y) + .isSameAs(position2); + } + } + } +} diff --git a/src/test/java/janggi/domain/RowTest.java b/src/test/java/janggi/domain/RowTest.java new file mode 100644 index 0000000000..0c8c3dfa3a --- /dev/null +++ b/src/test/java/janggi/domain/RowTest.java @@ -0,0 +1,29 @@ +package janggi.domain; + +import janggi.exception.RowOutOfRangeException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class RowTest { + @Test + void 장기판의_x좌표_범위를_벗어나면_예외가_발생한다() { + Assertions.assertThatThrownBy(() -> Row.of(9)).isInstanceOf(RowOutOfRangeException.class); + Assertions.assertThatThrownBy(() -> Row.of(-1)).isInstanceOf(RowOutOfRangeException.class); + } + + @Test + void 장기판의_x좌표_범위_내라면_예외가_발생하지_않는다() { + Row row1 = Row.of(0); + Row row2 = Row.of(8); + + Assertions.assertThat(row1.getRow()).isEqualTo(0); + Assertions.assertThat(row2.getRow()).isEqualTo(8); + } + + @Test + void 동등성_테스트() { + Row row = Row.of(1); + + Assertions.assertThat(row).isEqualTo(Row.of(1)); + } +} diff --git a/src/test/java/janggi/domain/movestorage/ChaMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/ChaMoveStorageTest.java new file mode 100644 index 0000000000..be75c1103f --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/ChaMoveStorageTest.java @@ -0,0 +1,149 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class ChaMoveStorageTest { + private static class FakeBoard implements BoardState { + @Override + public boolean hasPieceAt(Position position) { + return false; + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + private static class ObstaclFakeBoard implements BoardState { + private final List obstacles; + + public ObstaclFakeBoard(List obstacles) { + this.obstacles = new ArrayList<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.contains(position); + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_멱이_없다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + BoardState boardState = new FakeBoard(); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_멱이_없다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_멱이_없다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_멱이_없다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 차가_아예_갈_수_없는_행마면_실패를_반환한다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(1), Column.of(3)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높으면서_멱이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + Position obstacl = Position.of(Row.of(0), Column.of(2)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstacl)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮으면서_멱이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + Position obstacl = Position.of(Row.of(0), Column.of(1)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstacl)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높으면서_멱이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + Position obstacl = Position.of(Row.of(2), Column.of(0)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstacl)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮으면서_멱이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new ChaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + Position obstacl = Position.of(Row.of(1), Column.of(0)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstacl)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/movestorage/GungAndSaMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/GungAndSaMoveStorageTest.java new file mode 100644 index 0000000000..a946c0704c --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/GungAndSaMoveStorageTest.java @@ -0,0 +1,250 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import janggi.domain.Team; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class GungAndSaMoveStorageTest { + private static class ObstaclFakeBoard implements BoardState { + private final Map obstacles; + + public ObstaclFakeBoard(Map obstacles) { + this.obstacles = new HashMap<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.containsKey(position); + } + + @Override + public Piece getPieceAt(Position position) { + return obstacles.get(position); + } + } + + @Test + void 초나라_왕과_사는_궁성_내부에서_위로_한_칸_이동할_수_있다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(8)); + Position to = Position.of(Row.of(4), Column.of(7)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 초나라_왕과_사는_궁성_내부에서_아래로_한_칸_이동할_수_있다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(8)); + Position to = Position.of(Row.of(4), Column.of(9)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 초나라_왕과_사는_궁성_내부에서_왼쪽으로_한_칸_이동할_수_있다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(8)); + Position to = Position.of(Row.of(3), Column.of(8)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 초나라_왕과_사는_궁성_내부에서_오른쪽으로_한_칸_이동할_수_있다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(8)); + Position to = Position.of(Row.of(5), Column.of(8)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 초나라_왕과_사는_궁성의_좌측_상단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(7)); + Position to = Position.of(Row.of(2), Column.of(7)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성의_좌측_중단에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(8)); + Position to = Position.of(Row.of(2), Column.of(8)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성의_좌측_하단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(9)); + Position to = Position.of(Row.of(2), Column.of(9)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성의_우측_상단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(7)); + Position to = Position.of(Row.of(6), Column.of(7)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성의_우측_중단에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(8)); + Position to = Position.of(Row.of(6), Column.of(8)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성의_우측_하단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(9)); + Position to = Position.of(Row.of(6), Column.of(9)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 초나라_왕과_사는_궁성_제일_앞줄에서_앞으로_한_칸_더_전진하여_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(7)); + Position to = Position.of(Row.of(4), Column.of(6)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.CHO, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_좌측_상단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(2), Column.of(0)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_좌측_중단에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(1)); + Position to = Position.of(Row.of(2), Column.of(1)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_좌측_하단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(3), Column.of(2)); + Position to = Position.of(Row.of(2), Column.of(2)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_우측_상단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(0)); + Position to = Position.of(Row.of(6), Column.of(0)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_우측_중단에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(1)); + Position to = Position.of(Row.of(6), Column.of(1)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성의_우측_하단_끝에서_궁성_밖으로_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(5), Column.of(2)); + Position to = Position.of(Row.of(6), Column.of(2)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_왕과_사는_궁성_제일_앞줄에서_앞으로_한_칸_더_전진하여_이탈하는_경우_실패를_반환한다() { + // given + MoveStorage moveStorage = new GungAndSaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(2)); + Position to = Position.of(Row.of(4), Column.of(3)); + Piece piece = new Piece(new GungAndSaMoveStorage(), Team.HAN, 9, "士"); + BoardState boardState = new ObstaclFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/movestorage/JolMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/JolMoveStorageTest.java new file mode 100644 index 0000000000..d2801ee6db --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/JolMoveStorageTest.java @@ -0,0 +1,130 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import janggi.domain.Team; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class JolMoveStorageTest { + private static class FakeBoard implements BoardState { + @Override + public boolean hasPieceAt(Position position) { + return false; + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + private static class JolFakeBoard implements BoardState { + private final Map obstacles; + + public JolFakeBoard(Map obstacles) { + this.obstacles = new HashMap<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.containsKey(position); + } + + @Override + public Piece getPieceAt(Position position) { + return obstacles.get(position); + } + } + + @Test + void 초나라_졸은_위로_한_칸_전진할_수_있다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(6)); + Position to = Position.of(Row.of(4), Column.of(5)); + Piece piece = new Piece(new JolMoveStorage(), Team.CHO, 2, "卒"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 초나라_졸은_아래로_한_칸_후퇴할_수_없다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(6)); + Position to = Position.of(Row.of(4), Column.of(7)); + Piece piece = new Piece(new JolMoveStorage(), Team.CHO, 2, "卒"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 한나라_졸은_아래로_한_칸_전진할_수_있다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(4), Column.of(4)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "兵"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 한나라_졸은_위로_한_칸_후퇴할_수_없다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(4), Column.of(2)); + Piece piece = new Piece(new JolMoveStorage(), Team.HAN, 2, "兵"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 졸은_왼쪽으로_이동할_수_있다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(3), Column.of(3)); + Piece piece = new Piece(new JolMoveStorage(), Team.CHO, 2, "卒"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 졸은_오른쪽으로_이동할_수_있다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(5), Column.of(3)); + Piece piece = new Piece(new JolMoveStorage(), Team.CHO, 2, "卒"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 이동할_수_없는_행마면_실패를_반환한다() { + // given + MoveStorage moveStorage = new JolMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(3)); + Position to = Position.of(Row.of(5), Column.of(5)); + Piece piece = new Piece(new JolMoveStorage(), Team.CHO, 2, "卒"); + BoardState boardState = new JolFakeBoard(Map.of(from, piece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/movestorage/MaMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/MaMoveStorageTest.java new file mode 100644 index 0000000000..1fae8c852c --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/MaMoveStorageTest.java @@ -0,0 +1,169 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class MaMoveStorageTest { + private static class FakeBoard implements BoardState { + @Override + public boolean hasPieceAt(Position position) { + return false; + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + private static class ObstaclFakeBoard implements BoardState { + private final List obstacles; + + public ObstaclFakeBoard(List obstacles) { + this.obstacles = new ArrayList<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.contains(position); + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + @Test + void 행마_성공_테스트() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(3)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 마가_갈_수_없는_좌표가_들어오면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(4)); + BoardState boardState = new FakeBoard(); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_위로_2칸_왼쪽으로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(3), Column.of(2)); + + Position obstaclPosition = Position.of(Row.of(4), Column.of(3)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_위로_2칸_오른쪽으로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(5), Column.of(2)); + + Position obstaclPosition = Position.of(Row.of(4), Column.of(3)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_오른쪽으로_2칸_위로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(3)); + + Position obstaclPosition = Position.of(Row.of(5), Column.of(4)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_오른쪽으로_2칸_아래로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(5)); + + Position obstaclPosition = Position.of(Row.of(5), Column.of(4)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_아래로_2칸_오른쪽으로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(5), Column.of(6)); + + Position obstaclPosition = Position.of(Row.of(4), Column.of(5)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_아래로_2칸_왼쪽으로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(3), Column.of(6)); + + Position obstaclPosition = Position.of(Row.of(4), Column.of(5)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_왼쪽으로_2칸_아래로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(2), Column.of(5)); + + Position obstaclPosition = Position.of(Row.of(3), Column.of(4)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 마가_왼쪽으로_2칸_위로_1칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new MaMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(2), Column.of(3)); + + Position obstaclPosition = Position.of(Row.of(3), Column.of(4)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/movestorage/PoMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/PoMoveStorageTest.java new file mode 100644 index 0000000000..e5edca1792 --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/PoMoveStorageTest.java @@ -0,0 +1,511 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import janggi.domain.Team; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class PoMoveStorageTest { + + private static class FakeBoard implements BoardState { + @Override + public boolean hasPieceAt(Position position) { + return false; + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + private static class ObstaclFakeBoard implements BoardState { + private final Map obstacles; + + public ObstaclFakeBoard(Map obstacles) { + this.obstacles = new HashMap<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.containsKey(position); + } + + @Override + public Piece getPieceAt(Position position) { + return obstacles.get(position); + } + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포가_아니다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(1)); + Piece obstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포가_아니다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(1)); + Piece obstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포가_아니다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece obstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포가_아니다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece obstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 포가_아예_갈_수_없는_행마면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(1), Column.of(3)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(1)); + Piece obstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_중간에_기물이_없으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_중간에_기물이_없으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_중간에_기물이_없으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_중간에_기물이_없으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(3)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(0)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(3), Column.of(0)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(0)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_중간_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(2)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_중간_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(0), Column.of(2)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_중간_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_중간_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position obstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece obstaclePiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + BoardState boardState = new ObstaclFakeBoard(Map.of(obstaclePosition, obstaclePiece)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + Position pathObstaclePosition = Position.of(Row.of(0), Column.of(2)); + Piece pathObstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position targetPosition = Position.of(Row.of(0), Column.of(3)); + Piece targetPiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + Map initialPieces = Map.of( + pathObstaclePosition, pathObstaclePiece, + targetPosition, targetPiece + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position pathObstaclePosition = Position.of(Row.of(0), Column.of(2)); + Piece pathObstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position targetPosition = Position.of(Row.of(0), Column.of(0)); + Piece targetPiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + Map initialPieces = Map.of( + pathObstaclePosition, pathObstaclePiece, + targetPosition, targetPiece + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + Position pathObstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece pathObstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position targetPosition = Position.of(Row.of(3), Column.of(0)); + Piece targetPiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + Map initialPieces = Map.of( + pathObstaclePosition, pathObstaclePiece, + targetPosition, targetPiece + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_s낮고_중간에_포가_아닌_기물이_한_개_있고_도착지점_기물이_포면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position pathObstaclePosition = Position.of(Row.of(2), Column.of(0)); + Piece pathObstaclePiece = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position targetPosition = Position.of(Row.of(0), Column.of(0)); + Piece targetPiece = new Piece(new PoMoveStorage(), Team.HAN, 7, "包"); + + Map initialPieces = Map.of( + pathObstaclePosition, pathObstaclePiece, + targetPosition, targetPiece + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_높고_중간에_기물이_2개_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(3)); + + Position pathObstaclePosition1 = Position.of(Row.of(0), Column.of(1)); + Piece pathObstaclePiece1 = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position pathObstaclePosition2 = Position.of(Row.of(0), Column.of(2)); + Piece pathObstaclePiece2 = new Piece(new MaMoveStorage(), Team.HAN, 5, "馬"); + + Map initialPieces = Map.of( + pathObstaclePosition1, pathObstaclePiece1, + pathObstaclePosition2, pathObstaclePiece2 + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_X축은_같고_Y축은_도착지점이_더_낮고_중간에_기물이_2개_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(3)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position pathObstaclePosition1 = Position.of(Row.of(0), Column.of(1)); + Piece pathObstaclePiece1 = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position pathObstaclePosition2 = Position.of(Row.of(0), Column.of(2)); + Piece pathObstaclePiece2 = new Piece(new MaMoveStorage(), Team.HAN, 5, "馬"); + + Map initialPieces = Map.of( + pathObstaclePosition1, pathObstaclePiece1, + pathObstaclePosition2, pathObstaclePiece2 + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_높고_중간에_기물이_2개_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(0), Column.of(0)); + Position to = Position.of(Row.of(3), Column.of(0)); + + Position pathObstaclePosition1 = Position.of(Row.of(1), Column.of(0)); + Piece pathObstaclePiece1 = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position pathObstaclePosition2 = Position.of(Row.of(2), Column.of(0)); + Piece pathObstaclePiece2 = new Piece(new MaMoveStorage(), Team.HAN, 5, "馬"); + + Map initialPieces = Map.of( + pathObstaclePosition1, pathObstaclePiece1, + pathObstaclePosition2, pathObstaclePiece2 + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 출발지점과_도착지점이_Y축은_같고_X축은_도착지점이_더_낮고_중간에_기물이_2개_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new PoMoveStorage(); + + Position from = Position.of(Row.of(3), Column.of(0)); + Position to = Position.of(Row.of(0), Column.of(0)); + + Position pathObstaclePosition1 = Position.of(Row.of(1), Column.of(0)); + Piece pathObstaclePiece1 = new Piece(new JolMoveStorage(), Team.HAN, 2, "卒"); + + Position pathObstaclePosition2 = Position.of(Row.of(2), Column.of(0)); + Piece pathObstaclePiece2 = new Piece(new MaMoveStorage(), Team.HAN, 5, "馬"); + + Map initialPieces = Map.of( + pathObstaclePosition1, pathObstaclePiece1, + pathObstaclePosition2, pathObstaclePiece2 + ); + + BoardState boardState = new ObstaclFakeBoard(initialPieces); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/movestorage/SangMoveStorageTest.java b/src/test/java/janggi/domain/movestorage/SangMoveStorageTest.java new file mode 100644 index 0000000000..bd29fe661c --- /dev/null +++ b/src/test/java/janggi/domain/movestorage/SangMoveStorageTest.java @@ -0,0 +1,170 @@ +package janggi.domain.movestorage; + +import janggi.domain.BoardState; +import janggi.domain.Column; +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.Row; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class SangMoveStorageTest { + + private static class FakeBoard implements BoardState { + @Override + public boolean hasPieceAt(Position position) { + return false; + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + private static class ObstaclFakeBoard implements BoardState { + private final List obstacles; + + public ObstaclFakeBoard(List obstacles) { + this.obstacles = new ArrayList<>(obstacles); + } + + @Override + public boolean hasPieceAt(Position position) { + return obstacles.contains(position); + } + + @Override + public Piece getPieceAt(Position position) { + return null; + } + } + + @Test + void 행마_성공_테스트() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(7), Column.of(2)); + BoardState boardState = new FakeBoard(); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isTrue(); + } + + @Test + void 상이_갈_수_없는_좌표가_들어오면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(7), Column.of(3)); + BoardState boardState = new FakeBoard(); + + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_위로_3칸_왼쪽으로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(2), Column.of(1)); + + Position obstaclPosition = Position.of(Row.of(3), Column.of(2)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_위로_3칸_오른쪽으로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(1)); + + Position obstaclPosition = Position.of(Row.of(5), Column.of(2)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_오른쪽으로_3칸_위로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(7), Column.of(2)); + + Position obstaclPosition = Position.of(Row.of(6), Column.of(3)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_오른쪽으로_3칸_아래로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(7), Column.of(6)); + + Position obstaclPosition = Position.of(Row.of(6), Column.of(5)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_아래로_3칸_오른쪽으로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(6), Column.of(7)); + + Position obstaclPosition = Position.of(Row.of(5), Column.of(6)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_아래로_3칸_왼쪽으로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + // given + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(2), Column.of(7)); + + Position obstaclPosition = Position.of(Row.of(3), Column.of(6)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_왼쪽으로_3칸_아래로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(1), Column.of(6)); + + Position obstaclPosition = Position.of(Row.of(2), Column.of(5)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } + + @Test + void 상이_왼쪽으로_3칸_위로_2칸_이동하려_할_때_경로에_기물이_있으면_실패를_반환한다() { + MoveStorage moveStorage = new SangMoveStorage(); + Position from = Position.of(Row.of(4), Column.of(4)); + Position to = Position.of(Row.of(1), Column.of(2)); + + Position obstaclPosition = Position.of(Row.of(2), Column.of(3)); + BoardState boardState = new ObstaclFakeBoard(List.of(obstaclPosition)); + // when & then + assertThat(moveStorage.canMove(from, to, boardState)).isFalse(); + } +}