diff --git a/README.md b/README.md index 9775dda0ae..053acf52cf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,44 @@ # java-janggi 장기 미션 저장소 + +version : 1.2 (구현의 어려움에 따른 도메인 변경 : 돌아가는 코드를 우선 만들어보기) + +# 시나리오 + +1. 한나라 사용자가 상차림을 선택한다. +2. 초나라 사용자가 상차림을 선택한다. +3. 초나라 시작 + 1. 이동할 기물의 좌표를 입력한다. (무르기 : q) + + 현재 보드를 보여준다. + + ex) 졸을 선택했습니다. 졸은 현재 (1,1)에 있습니다. + + 2. 해당 기물이 이동할 수 있는 좌표를 보여준다. (기물 다시선택 : r) + - 만약에 아무데도 이동 불가능하면 다시 a로 돌아가서 선택하게 한다. + 3. 보여진 좌표 중 이동할 좌표를 입력한다. + - 이동할 좌표를 입력해주세요.(예시: 3, 5) + 4. 해당 좌표로 이동한다. +4. 한나라 시작 + + 초나라와 동일 + +# 도메인 구현 리스트 + +- **보드** + - [x] 상차림은 마상마상으로 고정하여 초기화 한다. (추후 추가 예정) + - [x] 해당 기물의 이동 가능 여부를 판단한다 + - 멱에 다른 기물이 있으면 안 되는 경우: 마,상, 졸/병, 차, 장, 사 + - 멱 사이에 하나의 기물이 있어야 하는 경우: 포 + - 그 기물이 포여서는 안 된다. + - 이동하고자 하는 좌표 자체가 포 or 아군이면 안 된다. + - [x] 기물을 이동시킨다. +- **좌표** + - [x] 보드 내의 좌표 범위를 검증한다. +- **기물** + - [x] 해당 기물은 팀과 기물 타입을 가진다. + - [x] PieceType(enum) 작성 + - [x] 팀 Team(enum) 작성 +- **규칙** + - [x] 각 기물은 고유의 규칙을 가진다. diff --git a/src/main/java/janggi/Application.java b/src/main/java/janggi/Application.java new file mode 100644 index 0000000000..59fa0394a5 --- /dev/null +++ b/src/main/java/janggi/Application.java @@ -0,0 +1,10 @@ +package janggi; + +import janggi.controller.JanggiController; + +public class Application { + public static void main(String[] args) { + JanggiController janggiController = new JanggiController(); + janggiController.run(); + } +} diff --git a/src/main/java/janggi/controller/JanggiController.java b/src/main/java/janggi/controller/JanggiController.java new file mode 100644 index 0000000000..1100525e01 --- /dev/null +++ b/src/main/java/janggi/controller/JanggiController.java @@ -0,0 +1,92 @@ +package janggi.controller; + +import janggi.domain.board.Board; +import janggi.domain.board.BoardFormation; +import janggi.domain.board.BoardInitiator; +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.route.RouteChecker; +import janggi.view.InputView; +import janggi.view.OutputView; +import java.util.List; +import java.util.function.Supplier; + +public class JanggiController { + + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + private final BoardInitiator boardInitiator = new BoardInitiator(); + private final RouteChecker routeChecker = new RouteChecker(); + + public void run() { + Board board = new Board(); + + choiceBoardFormation(board); + + boolean isChoTurn = true; + + outputView.printBoard(board.getBoard()); + + while (true) { + playGame(isChoTurn, board); + + isChoTurn = changeTurn(isChoTurn); + } + } + + private void choiceBoardFormation(Board board) { + askBoardFormation(board, Team.HAN); + askBoardFormation(board, Team.CHO); + } + + private void askBoardFormation(Board board, Team team) { + outputView.printBoardFormation(team); + int boardFormationChoice = inputView.readBoardFormationChoice(); + BoardFormation formation = BoardFormation.selectByChoice(boardFormationChoice); + boardInitiator.initializeByFormation(board, formation, team); + } + + private void playGame(boolean isChoTurn, Board board) { + outputView.printTurnMessage(isChoTurn); + + Position movePiecePosition = doLoop(() -> askMovePiecePosition(board)); + + List availablePositions = routeChecker.findAvailablePositions(board, movePiecePosition); + + outputView.printAvailablePositions(board.getBoard(), availablePositions); + + Position movePosition = doLoop(() -> askMovePosition(board, movePiecePosition)); + + board.movePiece(movePiecePosition, movePosition); + + outputView.printBoard(board.getBoard()); + } + + private boolean changeTurn(boolean isChoTurn) { + return !isChoTurn; + } + + private Position askMovePosition(Board board, Position movePiecePosition) { + outputView.printMoveChoiceInfo(); + Position position = inputView.readPosition(); + routeChecker.validateDestination(board, movePiecePosition, position); + return position; + } + + private Position askMovePiecePosition(Board board) { + outputView.printMoveInfo(); + Position position = inputView.readPosition(); + routeChecker.findAvailablePositions(board, position); + return position; + } + + private T doLoop(Supplier inputFunction) { + while (true) { + try { + return inputFunction.get(); + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/janggi/domain/board/Board.java b/src/main/java/janggi/domain/board/Board.java new file mode 100644 index 0000000000..671b3bb5a5 --- /dev/null +++ b/src/main/java/janggi/domain/board/Board.java @@ -0,0 +1,33 @@ +package janggi.domain.board; + +import janggi.domain.common.Position; +import janggi.domain.piece.Piece; +import java.util.HashMap; +import java.util.Map; + +public class Board { + + private final Map board = new HashMap<>(); + + public void place(Position position, Piece piece) { + board.put(position, piece); + } + + public Map getBoard() { + return board; + } + + public void movePiece(Position movePiecePosition, Position destination) { + Piece piece = board.get(movePiecePosition); + board.remove(movePiecePosition); + board.put(destination, piece); + } + + public Piece pieceAt(Position position) { + return board.get(position); + } + + public boolean hasPiece(Position position) { + return board.containsKey(position); + } +} diff --git a/src/main/java/janggi/domain/board/BoardFormation.java b/src/main/java/janggi/domain/board/BoardFormation.java new file mode 100644 index 0000000000..92f2d0ee46 --- /dev/null +++ b/src/main/java/janggi/domain/board/BoardFormation.java @@ -0,0 +1,54 @@ +package janggi.domain.board; + +import java.util.List; + +public enum BoardFormation { + MA_SANG_MA_SANG(1, "마상마상", List.of(2, 7), List.of(3, 8)), + MA_SANG_SANG_MA(2, "마상상마", List.of(2, 8), List.of(3, 7)), + SANG_MA_SANG_MA(3, "상마상마", List.of(3, 8), List.of(2, 7)), + SANG_MA_MA_SANG(4, "상마마상", List.of(3, 7), List.of(2, 8)); + + private final int choice; + private final String name; + private final List maXPositions; + private final List sangXPositions; + + BoardFormation(int choice, String name, List maXPositions, List sangXPositions) { + this.choice = choice; + this.name = name; + this.maXPositions = maXPositions; + this.sangXPositions = sangXPositions; + } + + public static BoardFormation selectByChoice(int choice) { + if (choice == 1) { + return MA_SANG_MA_SANG; + } + if (choice == 2) { + return MA_SANG_SANG_MA; + } + if (choice == 3) { + return SANG_MA_SANG_MA; + } + if (choice == 4) { + return SANG_MA_MA_SANG; + } + throw new IllegalArgumentException("[ERROR] 잘못된 번호를 입력하셨습니다."); + } + + public int getChoice() { + return choice; + } + + public String getName() { + return name; + } + + public List getMaXPositions() { + return maXPositions; + } + + public List getSangXPositions() { + return sangXPositions; + } +} diff --git a/src/main/java/janggi/domain/board/BoardInitiator.java b/src/main/java/janggi/domain/board/BoardInitiator.java new file mode 100644 index 0000000000..327b51a58b --- /dev/null +++ b/src/main/java/janggi/domain/board/BoardInitiator.java @@ -0,0 +1,73 @@ +package janggi.domain.board; + +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BoardInitiator { + + private static final int MAX_Y = 11; + + private final Map> defaultXPositions = Map.of( + PieceType.KING, List.of(5), + PieceType.SA, List.of(4, 6), + PieceType.CHA, List.of(1, 9), + PieceType.PO, List.of(2, 8), + PieceType.ZOL, List.of(1, 3, 5, 7, 9) + ); + + private final Map defaultYPosition = Map.of( + PieceType.KING, 9, + PieceType.SA, 10, + PieceType.SANG, 10, + PieceType.MA, 10, + PieceType.CHA, 10, + PieceType.PO, 8, + PieceType.ZOL, 7 + ); + + public void initializeByFormation(Board board, BoardFormation formation, Team team) { + Map> xPositions = initXPositionsByFormation(formation); + placeByPieceType(board, xPositions, team); + } + + private Map> initXPositionsByFormation(BoardFormation formation) { + Map> result = new HashMap<>(); + for (PieceType pieceType : PieceType.values()) { + if (pieceType == PieceType.MA) { + result.put(pieceType, formation.getMaXPositions()); + continue; + } + if (pieceType == PieceType.SANG) { + result.put(pieceType, formation.getSangXPositions()); + continue; + } + result.put(pieceType, defaultXPositions.get(pieceType)); + } + return result; + } + + private void placeByPieceType(Board board, Map> hanXPositions, Team team) { + for (PieceType pieceType : PieceType.values()) { + placeByPieceType(board, hanXPositions, team, pieceType); + } + } + + private void placeByPieceType(Board board, Map> hanXPositions, Team team, + PieceType pieceType) { + for (Integer x : hanXPositions.get(pieceType)) { + if (team == Team.CHO) { + Position position = new Position(x, defaultYPosition.get(pieceType)); + board.place(position, new Piece(team, pieceType)); + } + if (team == Team.HAN) { + Position position = new Position(x, MAX_Y - defaultYPosition.get(pieceType)); + board.place(position, new Piece(team, pieceType)); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/janggi/domain/common/Direction.java b/src/main/java/janggi/domain/common/Direction.java new file mode 100644 index 0000000000..3b96fd7b66 --- /dev/null +++ b/src/main/java/janggi/domain/common/Direction.java @@ -0,0 +1,26 @@ +package janggi.domain.common; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public enum Direction { + UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0), + UP_LEFT(-1, -1), UP_RIGHT(1, -1), DOWN_LEFT(-1, 1), DOWN_RIGHT(1, 1); + + private final int x; + private final int y; + + Direction(int x, int y) { + this.x = x; + this.y = y; + } + + public Optional nextPosition(Position position) { + return position.applyDirection(x, y); + } + + public void nextContinuousPosition(Position position, Map> continuousRoute) { + position.applyContinuousDirection(x, y, continuousRoute); + } +} diff --git a/src/main/java/janggi/domain/common/Position.java b/src/main/java/janggi/domain/common/Position.java new file mode 100644 index 0000000000..f02f4b90e5 --- /dev/null +++ b/src/main/java/janggi/domain/common/Position.java @@ -0,0 +1,65 @@ +package janggi.domain.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class Position { + + private static final int MIN_X = 1; + private static final int MAX_X = 9; + private static final int MIN_Y = 1; + private static final int MAX_Y = 10; + private final int x; + private final int y; + + public Position(int x, int y) { + validateBoundary(x, y); + this.x = x; + this.y = y; + } + + public Optional applyDirection(int dx, int dy) { + if (isInsideBoundary(x + dx, y + dy)) { + return Optional.of(new Position(x + dx, y + dy)); + } + return Optional.empty(); + } + + public void applyContinuousDirection(int dx, int dy, Map> continuousRoute) { + List result = new ArrayList<>(); + int nextX = x + dx; + int nextY = y + dy; + while (isInsideBoundary(nextX, nextY)) { + result.add(new Position(nextX, nextY)); + continuousRoute.put(new Position(nextX, nextY), new ArrayList<>(result)); + nextX += dx; + nextY += dy; + } + } + + private void validateBoundary(int x, int y) { + if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) { + throw new IllegalArgumentException("[ERROR] 보드 범위를 벗어났습니다."); + } + } + + private boolean isInsideBoundary(int x, int y) { + return x >= MIN_X && x <= MAX_X && y >= MIN_Y && y <= MAX_Y; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Position position)) { + return false; + } + return x == position.x && y == position.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} diff --git a/src/main/java/janggi/domain/common/Team.java b/src/main/java/janggi/domain/common/Team.java new file mode 100644 index 0000000000..14e721811a --- /dev/null +++ b/src/main/java/janggi/domain/common/Team.java @@ -0,0 +1,16 @@ +package janggi.domain.common; + +public enum Team { + CHO("초나라"), + HAN("한나라"); + + private final String name; + + Team(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/janggi/domain/piece/Piece.java b/src/main/java/janggi/domain/piece/Piece.java new file mode 100644 index 0000000000..0b1c38d0cc --- /dev/null +++ b/src/main/java/janggi/domain/piece/Piece.java @@ -0,0 +1,58 @@ +package janggi.domain.piece; + +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; +import java.util.Objects; + +public class Piece { + + private final Team team; + private final PieceType pieceType; + + public Piece(Team team, PieceType pieceType) { + this.team = team; + this.pieceType = pieceType; + } + + public String getTeamName() { + return team.getName(); + } + + public String getPieceTypeName() { + return pieceType.getName(); + } + + public Team getTeam() { + return team; + } + + public boolean isCha() { + return pieceType == PieceType.CHA; + } + + public boolean isPo() { + return pieceType == PieceType.PO; + } + + public List findRoutes() { + return pieceType.findRoutes(team); + } + + public boolean isSameTeam(Piece other) { + return team == other.team; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Piece piece)) { + return false; + } + return team == piece.team && pieceType == piece.pieceType; + } + + @Override + public int hashCode() { + return Objects.hash(team, pieceType); + } +} diff --git a/src/main/java/janggi/domain/piece/PieceType.java b/src/main/java/janggi/domain/piece/PieceType.java new file mode 100644 index 0000000000..08c28b9aa6 --- /dev/null +++ b/src/main/java/janggi/domain/piece/PieceType.java @@ -0,0 +1,39 @@ +package janggi.domain.piece; + +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.ChaMoveRule; +import janggi.domain.piece.moveRules.KingMoveRule; +import janggi.domain.piece.moveRules.MaMoveRule; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.piece.moveRules.PoMoveRule; +import janggi.domain.piece.moveRules.SaMoveRule; +import janggi.domain.piece.moveRules.SangMoveRule; +import janggi.domain.piece.moveRules.ZolMoveRule; +import janggi.domain.route.Route; +import java.util.List; + +public enum PieceType { + KING("왕", new KingMoveRule()), + SA("사", new SaMoveRule()), + SANG("상", new SangMoveRule()), + MA("마", new MaMoveRule()), + CHA("차", new ChaMoveRule()), + PO("포", new PoMoveRule()), + ZOL("졸", new ZolMoveRule()); + + private final String name; + private final MoveRule moveRule; + + PieceType(String name, MoveRule moveRule) { + this.name = name; + this.moveRule = moveRule; + } + + public String getName() { + return name; + } + + public List findRoutes(Team team) { + return moveRule.findRoutes(team); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/ChaMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/ChaMoveRule.java new file mode 100644 index 0000000000..922df79a2d --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/ChaMoveRule.java @@ -0,0 +1,18 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class ChaMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + return List.of(route1, route2, route3, route4); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/KingMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/KingMoveRule.java new file mode 100644 index 0000000000..b9e9d52c17 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/KingMoveRule.java @@ -0,0 +1,22 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class KingMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + Route route5 = new Route(List.of(Direction.UP_LEFT)); + Route route6 = new Route(List.of(Direction.UP_RIGHT)); + Route route7 = new Route(List.of(Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.DOWN_RIGHT)); + return List.of(route1, route2, route3, route4, route5, route6, route7, route8); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/MaMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/MaMoveRule.java new file mode 100644 index 0000000000..6df28de425 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/MaMoveRule.java @@ -0,0 +1,22 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class MaMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP, Direction.UP_LEFT)); + Route route2 = new Route(List.of(Direction.UP, Direction.UP_RIGHT)); + Route route3 = new Route(List.of(Direction.RIGHT, Direction.UP_RIGHT)); + Route route4 = new Route(List.of(Direction.RIGHT, Direction.DOWN_RIGHT)); + Route route5 = new Route(List.of(Direction.DOWN, Direction.DOWN_RIGHT)); + Route route6 = new Route(List.of(Direction.DOWN, Direction.DOWN_LEFT)); + Route route7 = new Route(List.of(Direction.LEFT, Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.LEFT, Direction.UP_LEFT)); + return List.of(route1, route2, route3, route4, route5, route6, route7, route8); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/MoveRule.java b/src/main/java/janggi/domain/piece/moveRules/MoveRule.java new file mode 100644 index 0000000000..c85640a392 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/MoveRule.java @@ -0,0 +1,9 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public interface MoveRule { + List findRoutes(Team team); +} diff --git a/src/main/java/janggi/domain/piece/moveRules/PoMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/PoMoveRule.java new file mode 100644 index 0000000000..7f68209dbe --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/PoMoveRule.java @@ -0,0 +1,18 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class PoMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + return List.of(route1, route2, route3, route4); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/SaMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/SaMoveRule.java new file mode 100644 index 0000000000..c373bba292 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/SaMoveRule.java @@ -0,0 +1,22 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class SaMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + Route route5 = new Route(List.of(Direction.UP_LEFT)); + Route route6 = new Route(List.of(Direction.UP_RIGHT)); + Route route7 = new Route(List.of(Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.DOWN_RIGHT)); + return List.of(route1, route2, route3, route4, route5, route6, route7, route8); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/SangMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/SangMoveRule.java new file mode 100644 index 0000000000..2a2976e3b7 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/SangMoveRule.java @@ -0,0 +1,22 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class SangMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + Route route1 = new Route(List.of(Direction.UP, Direction.UP_LEFT, Direction.UP_LEFT)); + Route route2 = new Route(List.of(Direction.UP, Direction.UP_RIGHT, Direction.UP_RIGHT)); + Route route3 = new Route(List.of(Direction.RIGHT, Direction.UP_RIGHT, Direction.UP_RIGHT)); + Route route4 = new Route(List.of(Direction.RIGHT, Direction.DOWN_RIGHT, Direction.DOWN_RIGHT)); + Route route5 = new Route(List.of(Direction.DOWN, Direction.DOWN_RIGHT, Direction.DOWN_RIGHT)); + Route route6 = new Route(List.of(Direction.DOWN, Direction.DOWN_LEFT, Direction.DOWN_LEFT)); + Route route7 = new Route(List.of(Direction.LEFT, Direction.DOWN_LEFT, Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.LEFT, Direction.UP_LEFT, Direction.UP_LEFT)); + return List.of(route1, route2, route3, route4, route5, route6, route7, route8); + } +} diff --git a/src/main/java/janggi/domain/piece/moveRules/ZolMoveRule.java b/src/main/java/janggi/domain/piece/moveRules/ZolMoveRule.java new file mode 100644 index 0000000000..0fafb7d6d9 --- /dev/null +++ b/src/main/java/janggi/domain/piece/moveRules/ZolMoveRule.java @@ -0,0 +1,24 @@ +package janggi.domain.piece.moveRules; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.route.Route; +import java.util.List; + +public class ZolMoveRule implements MoveRule { + + @Override + public List findRoutes(Team team) { + if (team == Team.CHO) { + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.LEFT)); + Route route3 = new Route(List.of(Direction.RIGHT)); + return List.of(route1, route2, route3); + } + Route route1 = new Route(List.of(Direction.DOWN)); + Route route2 = new Route(List.of(Direction.LEFT)); + Route route3 = new Route(List.of(Direction.RIGHT)); + return List.of(route1, route2, route3); + } + +} diff --git a/src/main/java/janggi/domain/route/Route.java b/src/main/java/janggi/domain/route/Route.java new file mode 100644 index 0000000000..f01a53e776 --- /dev/null +++ b/src/main/java/janggi/domain/route/Route.java @@ -0,0 +1,52 @@ +package janggi.domain.route; + +import janggi.domain.common.Direction; +import janggi.domain.common.Position; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class Route { + + private final List routes; + + public Route(List routes) { + this.routes = routes; + } + + public List applyDirections(Position position) { + List routePositions = new ArrayList<>(); + Position startPosition = position; + for (Direction direction : routes) { + Optional nextPosition = direction.nextPosition(startPosition); + if (nextPosition.isEmpty()) { + return new ArrayList<>(); + } + startPosition = nextPosition.get(); + routePositions.add(startPosition); + } + return routePositions; + } + + public void applyContinuousDirections(Position position, Map> continuousRoutes) { + Position startPosition = position; + for (Direction direction : routes) { + direction.nextContinuousPosition(startPosition, continuousRoutes); + } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Route route)) { + return false; + } + return Objects.equals(routes, route.routes); + } + + @Override + public int hashCode() { + return Objects.hashCode(routes); + } +} diff --git a/src/main/java/janggi/domain/route/RouteChecker.java b/src/main/java/janggi/domain/route/RouteChecker.java new file mode 100644 index 0000000000..81f473b0a5 --- /dev/null +++ b/src/main/java/janggi/domain/route/RouteChecker.java @@ -0,0 +1,99 @@ +package janggi.domain.route; + +import janggi.domain.board.Board; +import janggi.domain.common.Position; +import janggi.domain.piece.Piece; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class RouteChecker { + private final RouteConverter routeConverter = new RouteConverter(); + + public List findAvailablePositions(Board board, Position position) { + Piece piece = board.pieceAt(position); + + Map> routePositions = routeConverter.convertToPosition(board, position); + + List availablePositions = calculateAvailablePositions(board, piece, routePositions); + + if (availablePositions.isEmpty()) { + throw new IllegalArgumentException("[ERROR] 이동할 수 없는 좌표입니다."); + } + return availablePositions; + } + + public void validateDestination(Board board, Position movePiecePosition, Position destination) { + List availablePositions = findAvailablePositions(board, movePiecePosition); + boolean hasPosition = false; + for (Position position : availablePositions) { + if (position.equals(destination)) { + hasPosition = true; + break; + } + } + if (hasPosition == false) { + throw new IllegalArgumentException("[ERROR] 이동 가능한 좌표 중에서 선택하세요."); + } + } + + private List calculateAvailablePositions(Board board, Piece piece, + Map> routePositions) { + List result = new ArrayList<>(); + for (Map.Entry> entry : routePositions.entrySet()) { + Position destination = entry.getKey(); + List route = entry.getValue(); + + if (canMove(board, piece, route, destination)) { + result.add(destination); + } + } + return result; + } + + private boolean canMove(Board board, Piece movePiece, List route, Position destination) { + if (movePiece.isPo()) { + return hasOneObstacleAndNotPo(board, route) + && !isDestinationIsMyTeam(board, destination, movePiece) + && !isDestinationIsPo(board, destination); + } + return !hasObstacleOnRoute(board, route) && !isDestinationIsMyTeam(board, destination, movePiece); + } + + private boolean isDestinationIsMyTeam(Board board, Position destination, Piece piece) { + Piece destinationPiece = board.pieceAt(destination); + if (board.hasPiece(destination)) { + return piece.isSameTeam(destinationPiece); + } + return false; + } + + private boolean hasObstacleOnRoute(Board board, List route) { + for (int i = 0; i < route.size() - 1; i++) { + if (board.hasPiece(route.get(i))) { + return true; + } + } + return false; + } + + private boolean isDestinationIsPo(Board board, Position destination) { + Piece destinationPiece = board.pieceAt(destination); + if (board.hasPiece(destination)) { + return destinationPiece.isPo(); + } + return false; + } + + private boolean hasOneObstacleAndNotPo(Board board, List route) { + int count = 0; + List obstacles = new ArrayList<>(); + for (int i = 0; i < route.size() - 1; i++) { + if (board.hasPiece(route.get(i))) { + count += 1; + obstacles.add(board.pieceAt(route.get(i))); + } + } + return count == 1 && !obstacles.getFirst().isPo(); + } +} diff --git a/src/main/java/janggi/domain/route/RouteConverter.java b/src/main/java/janggi/domain/route/RouteConverter.java new file mode 100644 index 0000000000..aef4bcb5ca --- /dev/null +++ b/src/main/java/janggi/domain/route/RouteConverter.java @@ -0,0 +1,39 @@ +package janggi.domain.route; + +import janggi.domain.board.Board; +import janggi.domain.common.Position; +import janggi.domain.piece.Piece; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RouteConverter { + + public Map> convertToPosition(Board board, Position position) { + Piece piece = board.pieceAt(position); + if (piece.isCha() || piece.isPo()) { + return convertToContinuousRoutes(position, piece.findRoutes()); + } + return convertToFixedRoutes(position, piece.findRoutes()); + } + + private Map> convertToFixedRoutes(Position position, List routes) { + Map> result = new HashMap<>(); + for (Route route : routes) { + List positionRoute = route.applyDirections(position); + if (positionRoute.isEmpty()) { + continue; + } + result.put(positionRoute.getLast(), positionRoute); + } + return result; + } + + private Map> convertToContinuousRoutes(Position position, List routes) { + Map> result = new HashMap<>(); + for (Route route : routes) { + route.applyContinuousDirections(position, result); + } + return result; + } +} diff --git a/src/main/java/janggi/view/InputView.java b/src/main/java/janggi/view/InputView.java new file mode 100644 index 0000000000..1413a04d06 --- /dev/null +++ b/src/main/java/janggi/view/InputView.java @@ -0,0 +1,21 @@ +package janggi.view; + +import janggi.domain.common.Position; +import java.util.Scanner; + +public class InputView { + private final Scanner scanner = new Scanner(System.in); + + public Position readPosition() { + System.out.println("예시: 1 7 "); + String input = scanner.nextLine(); + + String[] position = input.split(" "); + return new Position(Integer.parseInt(position[0]), Integer.parseInt(position[1])); + } + + public int readBoardFormationChoice() { + System.out.println("상차림 번호를 입력하세요"); + return Integer.parseInt(scanner.nextLine()); + } +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java new file mode 100644 index 0000000000..bd9b4fef57 --- /dev/null +++ b/src/main/java/janggi/view/OutputView.java @@ -0,0 +1,110 @@ +package janggi.view; + +import janggi.domain.board.BoardFormation; +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import java.util.List; +import java.util.Map; + +public class OutputView { + + public static final String HAN_COLOR = "\u001B[31m"; + public static final String CHO_COLOR = "\u001B[34m"; + public static final String AVAILABLE_COLOR = "\u001B[32m"; + public static final String RESET = "\u001B[0m"; + private static final String[] X_VALUES = {"1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + public static void printErrorMessage(String message) { + System.out.printf("%s%n", message); + } + + public void printBoard(Map board) { + System.out.print(" "); + for (int x = 1; x <= 9; x++) { + System.out.print(X_VALUES[x - 1] + " "); + } + System.out.println(); + + for (int y = 1; y <= 10; y++) { + System.out.print(String.format("%2d ", y)); + + for (int x = 1; x <= 9; x++) { + Position currentPos = new Position(x, y); + if (board.containsKey(currentPos)) { + Piece piece = board.get(currentPos); + String color = getColorByTeam(piece); + + System.out.print(color + piece.getPieceTypeName() + RESET + " "); + } else { + System.out.print(". "); + } + } + System.out.println(); + } + } + + public void printAvailablePositions(Map board, List availablePositions) { + System.out.print(" "); + for (int x = 1; x <= 9; x++) { + System.out.print(X_VALUES[x - 1] + " "); + } + System.out.println(); + + for (int y = 1; y <= 10; y++) { + System.out.print(String.format("%2d ", y)); + + for (int x = 1; x <= 9; x++) { + Position currentPos = new Position(x, y); + + if (availablePositions.contains(currentPos)) { + if (board.containsKey(currentPos)) { + System.out.print(AVAILABLE_COLOR + board.get(currentPos).getPieceTypeName() + RESET + " "); + } else { + System.out.print(AVAILABLE_COLOR + "O" + RESET + " "); + } + continue; + } + + if (board.containsKey(currentPos)) { + Piece piece = board.get(currentPos); + String color = getColorByTeam(piece); + System.out.print(color + piece.getPieceTypeName() + RESET + " "); + } else { + System.out.print(". "); + } + } + System.out.println(); + } + } + + public void printTurnMessage(boolean isChoTurn) { + if (isChoTurn) { + System.out.println(OutputView.CHO_COLOR + "\n현재 초나라 차례입니다" + OutputView.RESET); + } else { + System.out.println(OutputView.HAN_COLOR + "\n한나라 차례입니다" + OutputView.RESET); + } + } + + public void printMoveInfo() { + System.out.println("이동하고 싶은 기물의 좌표를 입력하세요."); + } + + public void printMoveChoiceInfo() { + System.out.println("이동하고자 하는 목표 지점의 좌표를 입력하세요."); + } + + private String getColorByTeam(Piece piece) { + if (piece.getTeam() == Team.CHO) { + return CHO_COLOR; + } + return HAN_COLOR; + } + + public void printBoardFormation(Team team) { + System.out.printf("%s 상차림을 선택하세요.%n", team.getName()); + for (BoardFormation boardFormation : BoardFormation.values()) { + System.out.printf("%d. %s%n", boardFormation.getChoice(), boardFormation.getName()); + } + } +} diff --git a/src/test/java/janggi/domain/board/BoardInitiatorTest.java b/src/test/java/janggi/domain/board/BoardInitiatorTest.java new file mode 100644 index 0000000000..073c217e57 --- /dev/null +++ b/src/test/java/janggi/domain/board/BoardInitiatorTest.java @@ -0,0 +1,92 @@ +package janggi.domain.board; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class BoardInitiatorTest { + @ParameterizedTest + @CsvSource({ + + "CHO,KING,5,9", + "CHO,SA,4,10", "CHO,SA,6,10", + "CHO,SANG,3,10", "CHO,SANG,8,10", + "CHO,MA,2,10", "CHO,MA,7,10", + "CHO,CHA,1,10", "CHO,CHA,9,10", + "CHO,PO,2,8", "CHO,PO,8,8", + "CHO,ZOL,1,7", "CHO,ZOL,3,7", "CHO,ZOL,5,7", "CHO,ZOL,7,7", "CHO,ZOL,9,7", + + "HAN,KING,5,2", + "HAN,SA,4,1", "HAN,SA,6,1", + "HAN,SANG,3,1", "HAN,SANG,8,1", + "HAN,MA,2,1", "HAN,MA,7,1", + "HAN,CHA,1,1", "HAN,CHA,9,1", + "HAN,PO,2,3", "HAN,PO,8,3", + "HAN,ZOL,1,4", "HAN,ZOL,3,4", "HAN,ZOL,5,4", "HAN,ZOL,7,4", "HAN,ZOL,9,4" + + }) + @DisplayName("초기화된 보드의 지정된 위치에 각 나라의 기물이 알맞게 배치되어 있다 (초, 한 모두 기본 상차림인 마상마상)") + void 보드_초기화_기물_배치_확인(Team team, PieceType pieceType, int x, int y) { + //given + BoardInitiator boardInitiator = new BoardInitiator(); + Board board = new Board(); + BoardFormation hanFormation = BoardFormation.MA_SANG_MA_SANG; + BoardFormation choFormation = BoardFormation.MA_SANG_MA_SANG; + Position position = new Position(x, y); + Piece piece = new Piece(team, pieceType); + + //when + boardInitiator.initializeByFormation(board, hanFormation, Team.HAN); + boardInitiator.initializeByFormation(board, choFormation, Team.CHO); + Map checkPiece = board.getBoard(); + + //then + assertThat(checkPiece.get(position)).isEqualTo(piece); + } + + @ParameterizedTest + @CsvSource({ + + "CHO,KING,5,9", + "CHO,SA,4,10", "CHO,SA,6,10", + "CHO,SANG,2,10", "CHO,SANG,7,10", + "CHO,MA,3,10", "CHO,MA,8,10", + "CHO,CHA,1,10", "CHO,CHA,9,10", + "CHO,PO,2,8", "CHO,PO,8,8", + "CHO,ZOL,1,7", "CHO,ZOL,3,7", "CHO,ZOL,5,7", "CHO,ZOL,7,7", "CHO,ZOL,9,7", + + "HAN,KING,5,2", + "HAN,SA,4,1", "HAN,SA,6,1", + "HAN,SANG,3,1", "HAN,SANG,8,1", + "HAN,MA,2,1", "HAN,MA,7,1", + "HAN,CHA,1,1", "HAN,CHA,9,1", + "HAN,PO,2,3", "HAN,PO,8,3", + "HAN,ZOL,1,4", "HAN,ZOL,3,4", "HAN,ZOL,5,4", "HAN,ZOL,7,4", "HAN,ZOL,9,4" + + }) + @DisplayName("한나라는 마상마상, 초나라는 상마상마의 상차람으로 보드를 초기화한다.") + void 한_마상마상_초_상마상마의_상차림으로_보드_초기화(Team team, PieceType pieceType, int x, int y) { + // given + BoardInitiator boardInitiator = new BoardInitiator(); + Board board = new Board(); + BoardFormation hanFormation = BoardFormation.MA_SANG_MA_SANG; + BoardFormation choFormation = BoardFormation.SANG_MA_SANG_MA; + Position position = new Position(x, y); + Piece piece = new Piece(team, pieceType); + + // when + boardInitiator.initializeByFormation(board, hanFormation, Team.HAN); + boardInitiator.initializeByFormation(board, choFormation, Team.CHO); + Map checkPiece = board.getBoard(); + + // then + assertThat(checkPiece.get(position)).isEqualTo(piece); + } +} diff --git a/src/test/java/janggi/domain/board/BoardTest.java b/src/test/java/janggi/domain/board/BoardTest.java new file mode 100644 index 0000000000..86a3fdcb14 --- /dev/null +++ b/src/test/java/janggi/domain/board/BoardTest.java @@ -0,0 +1,80 @@ +package janggi.domain.board; + + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BoardTest { + + @Test + @DisplayName("보드에 기물을 놓을 수 있다") + void 보드에_기물_두기() { + // given + Board board = new Board(); + Position position = new Position(1, 3); + Piece piece = new Piece(Team.CHO, PieceType.PO); + + // when + board.place(position, piece); + + // then + assertThat(board.hasPiece(position)).isTrue(); + assertThat(board.pieceAt(position)).isEqualTo(piece); + } + + @Test + @DisplayName("이동할 기물의 현재 좌표를 통해 목적지로 기물을 이동할 수 있다") + void 목적지_좌표로_기물_이동() { + // given + Board board = new Board(); + Position movePiecePosition = new Position(1, 5); + Position destinationPosition = new Position(1, 3); + + // when + board.movePiece(movePiecePosition, destinationPosition); + + // then + assertThat(board.hasPiece(movePiecePosition)).isFalse(); + assertThat(board.hasPiece(destinationPosition)).isTrue(); + } + + @Test + @DisplayName("좌표를 통해 기물을 알아낼 수 있다") + void 좌표를_통해_기물_확인() { + // given + Board board = new Board(); + Position position = new Position(2, 5); + Piece piece = new Piece(Team.HAN, PieceType.CHA); + board.place(position, piece); + + // when + Piece result = board.pieceAt(position); + + // then + assertThat(result).isEqualTo(piece); + } + + @Test + @DisplayName("해당 좌표에 기물이 있는지 확인할 수 있다") + void 좌표에_기물_있는지_판단() { + // given + Board board = new Board(); + Position position = new Position(1, 5); + Position emptyPosition = new Position(2, 7); + board.place(position, new Piece(Team.CHO, PieceType.PO)); + + // when + boolean result = board.hasPiece(position); + boolean result2 = board.hasPiece(emptyPosition); + + // then + assertThat(result).isTrue(); + assertThat(result2).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/common/PositionTest.java b/src/test/java/janggi/domain/common/PositionTest.java new file mode 100644 index 0000000000..701c3441c4 --- /dev/null +++ b/src/test/java/janggi/domain/common/PositionTest.java @@ -0,0 +1,67 @@ +package janggi.domain.common; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PositionTest { + + @Test + @DisplayName("보드 범위 내에 있는 좌표라면 좌표를 반환한다") + void 보드_범위_내_좌표_반환() { + // given + Position position = new Position(1, 9); + + // when + Optional result = position.applyDirection(0, 1); + + // then + assertThat(result).isPresent().contains(new Position(1, 10)); + } + + @Test + @DisplayName("보드 범위 밖에 있는 y좌표라면 좌표를 Optional") + void 보드_범위_밖_y좌표_반환_() { + // given + Position position = new Position(1, 10); + + // when + Optional result = position.applyDirection(0, 1); + + // then + assertThat(result).isEmpty(); + } + + @Test + @DisplayName("보드 범위 밖에 있는 x좌표라면 좌표를 Optional") + void 보드_범위_밖_x좌표_반환_() { + // given + Position position = new Position(9, 1); + + // when + Optional result = position.applyDirection(1, 0); + + // then + assertThat(result).isEmpty(); + } + + @Test + @DisplayName("보드 범위 내에 있는 좌표라면 연속적으로 추가할 수 있다") + void 보드_범위_내에_연속_좌표() { + // given + Position position = new Position(1, 5); + Map> continuousRoute = new HashMap<>(); + + // when + position.applyContinuousDirection(0, 1, continuousRoute); + + // then + assertThat(continuousRoute).hasSize(5) + .containsKey(new Position(1, 10)); + } +} diff --git a/src/test/java/janggi/domain/moveRules/ChaMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/ChaMoveRuleTest.java new file mode 100644 index 0000000000..9b7b1d9aff --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/ChaMoveRuleTest.java @@ -0,0 +1,34 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.ChaMoveRule; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ChaMoveRuleTest { + + @Test + @DisplayName("차는 직선으로 갈 수 있다") + void 차의_이동규칙() { + //given + MoveRule moveRule = new ChaMoveRule(); + Team team = Team.CHO; + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + List routes = List.of(route1, route2, route3, route4); + + //when + List chaRoutes = moveRule.findRoutes(team); + + //then + assertThat(chaRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/KingMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/KingMoveRuleTest.java new file mode 100644 index 0000000000..0ae0c9c8c5 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/KingMoveRuleTest.java @@ -0,0 +1,38 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.KingMoveRule; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class KingMoveRuleTest { + + @Test + @DisplayName("왕은 직선과 대각선으로 갈 수 있다.") + void 왕의_이동규칙() { + //given + MoveRule moveRule = new KingMoveRule(); + Team team = Team.CHO; + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + Route route5 = new Route(List.of(Direction.UP_LEFT)); + Route route6 = new Route(List.of(Direction.UP_RIGHT)); + Route route7 = new Route(List.of(Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.DOWN_RIGHT)); + List routes = List.of(route1, route2, route3, route4, route5, route6, route7, route8); + + //when + List kingRoutes = moveRule.findRoutes(team); + + //then + assertThat(kingRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/MaMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/MaMoveRuleTest.java new file mode 100644 index 0000000000..0c0cfff117 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/MaMoveRuleTest.java @@ -0,0 +1,38 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.MaMoveRule; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class MaMoveRuleTest { + + @Test + @DisplayName("마는 직선으로 한 번 대각선으로 한 번 갈 수 있다") + void 마_이동규칙() { + //given + MoveRule moveRule = new MaMoveRule(); + Team team = Team.HAN; + Route route1 = new Route(List.of(Direction.UP, Direction.UP_LEFT)); + Route route2 = new Route(List.of(Direction.UP, Direction.UP_RIGHT)); + Route route3 = new Route(List.of(Direction.RIGHT, Direction.UP_RIGHT)); + Route route4 = new Route(List.of(Direction.RIGHT, Direction.DOWN_RIGHT)); + Route route5 = new Route(List.of(Direction.DOWN, Direction.DOWN_RIGHT)); + Route route6 = new Route(List.of(Direction.DOWN, Direction.DOWN_LEFT)); + Route route7 = new Route(List.of(Direction.LEFT, Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.LEFT, Direction.UP_LEFT)); + + List routes = List.of(route1, route2, route3, route4, route5, route6, route7, route8); + //when + List maRoutes = moveRule.findRoutes(team); + + //then + assertThat(maRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/PoMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/PoMoveRuleTest.java new file mode 100644 index 0000000000..1128033a18 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/PoMoveRuleTest.java @@ -0,0 +1,34 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.piece.moveRules.PoMoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PoMoveRuleTest { + + @Test + @DisplayName("포는 직선으로 갈 수 있다") + void 포의_이동규칙() { + //given + MoveRule moveRule = new PoMoveRule(); + Team team = Team.CHO; + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + List routes = List.of(route1, route2, route3, route4); + + //when + List poRoutes = moveRule.findRoutes(team); + + //then + assertThat(poRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/SaMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/SaMoveRuleTest.java new file mode 100644 index 0000000000..5d5fb493c0 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/SaMoveRuleTest.java @@ -0,0 +1,38 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.piece.moveRules.SaMoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SaMoveRuleTest { + + @Test + @DisplayName("사는 직선과 대각선으로 갈 수 있다") + void 사의_이동규칙() { + //given + MoveRule moveRule = new SaMoveRule(); + Team team = Team.CHO; + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.RIGHT)); + Route route3 = new Route(List.of(Direction.DOWN)); + Route route4 = new Route(List.of(Direction.LEFT)); + Route route5 = new Route(List.of(Direction.UP_LEFT)); + Route route6 = new Route(List.of(Direction.UP_RIGHT)); + Route route7 = new Route(List.of(Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.DOWN_RIGHT)); + List routes = List.of(route1, route2, route3, route4, route5, route6, route7, route8); + + //when + List saRoutes = moveRule.findRoutes(team); + + //then + assertThat(saRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/SangMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/SangMoveRuleTest.java new file mode 100644 index 0000000000..af74bc5e0d --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/SangMoveRuleTest.java @@ -0,0 +1,38 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.piece.moveRules.SangMoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SangMoveRuleTest { + + @Test + @DisplayName("상은 직선으로 한 번 대각선으로 두 번 갈 수 있다") + void 상_이동규칙() { + //given + MoveRule moveRule = new SangMoveRule(); + Team team = Team.CHO; + Route route1 = new Route(List.of(Direction.UP, Direction.UP_LEFT, Direction.UP_LEFT)); + Route route2 = new Route(List.of(Direction.UP, Direction.UP_RIGHT, Direction.UP_RIGHT)); + Route route3 = new Route(List.of(Direction.RIGHT, Direction.UP_RIGHT, Direction.UP_RIGHT)); + Route route4 = new Route(List.of(Direction.RIGHT, Direction.DOWN_RIGHT, Direction.DOWN_RIGHT)); + Route route5 = new Route(List.of(Direction.DOWN, Direction.DOWN_RIGHT, Direction.DOWN_RIGHT)); + Route route6 = new Route(List.of(Direction.DOWN, Direction.DOWN_LEFT, Direction.DOWN_LEFT)); + Route route7 = new Route(List.of(Direction.LEFT, Direction.DOWN_LEFT, Direction.DOWN_LEFT)); + Route route8 = new Route(List.of(Direction.LEFT, Direction.UP_LEFT, Direction.UP_LEFT)); + List routes = List.of(route1, route2, route3, route4, route5, route6, route7, route8); + + //when + List sangRoutes = moveRule.findRoutes(team); + + //then + assertThat(sangRoutes).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/moveRules/ZolMoveRuleTest.java b/src/test/java/janggi/domain/moveRules/ZolMoveRuleTest.java new file mode 100644 index 0000000000..f2593b34ba --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/ZolMoveRuleTest.java @@ -0,0 +1,53 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Direction; +import janggi.domain.common.Team; +import janggi.domain.piece.moveRules.MoveRule; +import janggi.domain.piece.moveRules.ZolMoveRule; +import janggi.domain.route.Route; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ZolMoveRuleTest { + + @Test + @DisplayName("초나라 졸은 위양옆으로 갈 수 있다") + void 초나라_졸_이동규칙() { + //given + MoveRule moveRule = new ZolMoveRule(); + Team cho = Team.CHO; + Route route1 = new Route(List.of(Direction.UP)); + Route route2 = new Route(List.of(Direction.LEFT)); + Route route3 = new Route(List.of(Direction.RIGHT)); + + List routes = List.of(route1, route2, route3); + + //when + List zolPaths = moveRule.findRoutes(cho); + + //then + assertThat(zolPaths).isEqualTo(routes); + } + + @Test + @DisplayName("한나라 졸은 아래양옆으로 갈 수 있다") + void 한나라_졸_이동규칙() { + //given + MoveRule moveRule = new ZolMoveRule(); + Team han = Team.HAN; + Route route1 = new Route(List.of(Direction.DOWN)); + Route route2 = new Route(List.of(Direction.LEFT)); + Route route3 = new Route(List.of(Direction.RIGHT)); + + List routes = List.of(route1, route2, route3); + + //when + List zolPaths = moveRule.findRoutes(han); + + //then + assertThat(zolPaths).isEqualTo(routes); + } +} diff --git a/src/test/java/janggi/domain/piece/PieceTest.java b/src/test/java/janggi/domain/piece/PieceTest.java new file mode 100644 index 0000000000..d528220afd --- /dev/null +++ b/src/test/java/janggi/domain/piece/PieceTest.java @@ -0,0 +1,73 @@ +package janggi.domain.piece; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.common.Team; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PieceTest { + + @Test + @DisplayName("기물에는 초나라 졸이 있다.") + void 기물은_초나라_졸이_있음() { + //given + Team cho = Team.CHO; + PieceType zol = PieceType.ZOL; + Piece piece = new Piece(cho, zol); + + //when & then + assertThat(piece.getTeamName()).isEqualTo("초나라"); + assertThat(piece.getPieceTypeName()).isEqualTo("졸"); + } + + @Test + @DisplayName("기물이 차인지 확인할 수 있다") + void 기물이_차인지_확인() { + // given + Piece cha = new Piece(Team.CHO, PieceType.CHA); + Piece zol = new Piece(Team.HAN, PieceType.ZOL); + + // when + boolean isCha = cha.isCha(); + boolean isCha2 = zol.isCha(); + + //then + assertThat(isCha).isTrue(); + assertThat(isCha2).isFalse(); + } + + @Test + @DisplayName("기물이 포인지 확인할 수 있다") + void 기물이_포인지_확인() { + // given + Piece po = new Piece(Team.CHO, PieceType.PO); + Piece zol = new Piece(Team.HAN, PieceType.ZOL); + + // when + boolean isPo = po.isPo(); + boolean isPo2 = zol.isPo(); + + //then + assertThat(isPo).isTrue(); + assertThat(isPo2).isFalse(); + } + + @Test + @DisplayName("같은 팀인지 확인할 수 있다") + void 같은_팀인지_확인() { + // given + Piece sameTeam1 = new Piece(Team.CHO, PieceType.CHA); + Piece sameTeam2 = new Piece(Team.CHO, PieceType.MA); + Piece differentTeam1 = new Piece(Team.CHO, PieceType.PO); + Piece differentTeam2 = new Piece(Team.HAN, PieceType.KING); + + // when + boolean same = sameTeam1.isSameTeam(sameTeam2); + boolean different = differentTeam1.isSameTeam(differentTeam2); + + // then + assertThat(same).isTrue(); + assertThat(different).isFalse(); + } +} diff --git a/src/test/java/janggi/domain/route/RouteCheckerTest.java b/src/test/java/janggi/domain/route/RouteCheckerTest.java new file mode 100644 index 0000000000..3ea6537035 --- /dev/null +++ b/src/test/java/janggi/domain/route/RouteCheckerTest.java @@ -0,0 +1,522 @@ +package janggi.domain.route; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import janggi.domain.board.Board; +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RouteCheckerTest { + private final RouteChecker routeChecker = new RouteChecker(); + + @Test + @DisplayName("초나라 졸은 경로에 장애물이 없으면 위쪽, 왼쪽, 오른쪽으로 이동할 수 있다") + void 초나라_졸_장애물_없을때_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.ZOL)); + Position zolUp = new Position(5, 6); + Position zolLeft = new Position(4, 7); + Position zolRight = new Position(6, 7); + List rightAnswer = List.of(zolUp, zolRight, zolLeft); + + //when + List zolRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(rightAnswer).isEqualTo(zolRoutesPositions); + } + + @Test + @DisplayName("초나라 졸의 이동 방향에 아군 기물이 있으면 해당 방향으로는 이동할 수 없다") + void 초나라_졸_아군이_막고있을때_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.ZOL)); + Position zolUp = new Position(5, 6); + board.place(zolUp, new Piece(Team.CHO, PieceType.ZOL)); + Position zolLeft = new Position(4, 7); + Position zolRight = new Position(6, 7); + List rightAnswer = List.of(zolRight, zolLeft); + + //when + List zolRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(rightAnswer).isEqualTo(zolRoutesPositions); + } + + @Test + @DisplayName("마는 이동 경로(멱)에 장애물이 없으면 8방향 모두 이동할 수 있다") + void 마_장애물_없을때_8방향_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(4, 6); + board.place(position, new Piece(Team.CHO, PieceType.MA)); + Position maPos1 = new Position(3, 4); + Position maPos2 = new Position(5, 4); + Position maPos3 = new Position(6, 5); + Position maPos4 = new Position(6, 7); + Position maPos5 = new Position(3, 8); + Position maPos6 = new Position(5, 8); + Position maPos7 = new Position(2, 5); + Position maPos8 = new Position(2, 7); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7, maPos8); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("마는 이동 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 마_멱이_막혀있을때_해당_방향_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(4, 6); + board.place(position, new Piece(Team.CHO, PieceType.MA)); + board.place(new Position(4, 5), new Piece(Team.HAN, PieceType.CHA)); + Position maPos1 = new Position(6, 5); + Position maPos2 = new Position(6, 7); + Position maPos3 = new Position(3, 8); + Position maPos4 = new Position(5, 8); + Position maPos5 = new Position(2, 5); + Position maPos6 = new Position(2, 7); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("마의 최종 목적지에 아군 기물이 있으면 해당 좌표로 이동할 수 없다") + void 마_목적지에_아군_존재시_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(4, 6); + board.place(position, new Piece(Team.CHO, PieceType.MA)); + board.place(new Position(2, 7), new Piece(Team.CHO, PieceType.CHA)); + Position maPos1 = new Position(3, 4); + Position maPos2 = new Position(5, 4); + Position maPos3 = new Position(6, 5); + Position maPos4 = new Position(6, 7); + Position maPos5 = new Position(3, 8); + Position maPos6 = new Position(5, 8); + Position maPos7 = new Position(2, 5); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 이동 경로(멱)에 장애물이 없으면 8방향 모두 이동할 수 있다") + void 상_장애물_없을때_8방향_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.SANG)); + Position maPos1 = new Position(3, 4); + Position maPos2 = new Position(7, 4); + Position maPos3 = new Position(8, 5); + Position maPos4 = new Position(8, 9); + Position maPos5 = new Position(3, 10); + Position maPos6 = new Position(7, 10); + Position maPos7 = new Position(2, 5); + Position maPos8 = new Position(2, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7, maPos8); + + //when + List sangRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(sangRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 대각선 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 상_대각선_멱이_막혀있을때_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.SANG)); + board.place(new Position(6, 5), new Piece(Team.CHO, PieceType.ZOL)); + Position maPos1 = new Position(3, 4); + Position maPos2 = new Position(8, 5); + Position maPos3 = new Position(8, 9); + Position maPos4 = new Position(3, 10); + Position maPos5 = new Position(7, 10); + Position maPos6 = new Position(2, 5); + Position maPos7 = new Position(2, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7); + + //when + List sangRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(sangRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 직선 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 상_직선_멱이_막혀있을때_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.SANG)); + board.place(new Position(5, 6), new Piece(Team.HAN, PieceType.ZOL)); + Position maPos1 = new Position(8, 5); + Position maPos2 = new Position(8, 9); + Position maPos3 = new Position(3, 10); + Position maPos4 = new Position(7, 10); + Position maPos5 = new Position(2, 5); + Position maPos6 = new Position(2, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6); + + //when + List sangRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(sangRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상의 최종 목적지에 아군 기물이 있으면 해당 좌표로 이동할 수 없다") + void 상_목적지에_아군_존재시_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(5, 7); + board.place(position, new Piece(Team.CHO, PieceType.SANG)); + board.place(new Position(2, 9), new Piece(Team.CHO, PieceType.ZOL)); + Position maPos1 = new Position(3, 4); + Position maPos2 = new Position(7, 4); + Position maPos3 = new Position(8, 5); + Position maPos4 = new Position(8, 9); + Position maPos5 = new Position(3, 10); + Position maPos6 = new Position(7, 10); + Position maPos7 = new Position(2, 5); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7); + + //when + List sangRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(sangRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사는 이동경로에 장애물이 없는 곳으로 이동할 수 있다 (사이클1 규칙)") + void 사_장애물_없을때_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(4, 10); + board.place(position, new Piece(Team.CHO, PieceType.SA)); + Position maPos1 = new Position(4, 9); + Position maPos2 = new Position(5, 9); + Position maPos3 = new Position(5, 10); + Position maPos4 = new Position(3, 9); + Position maPos5 = new Position(3, 10); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사의 목적지에 아군 기물이 있으면 이동할 수 없다 (사이클1 규칙)") + void 사_목적지에_아군_존재시_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(4, 10); + board.place(position, new Piece(Team.CHO, PieceType.SA)); + board.place(new Position(5, 9), new Piece(Team.CHO, PieceType.KING)); + Position maPos1 = new Position(4, 9); + Position maPos2 = new Position(5, 10); + Position maPos3 = new Position(3, 9); + Position maPos4 = new Position(3, 10); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(4) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사의 목적지에 적군 기물이 있으면 이동할 수 있다 (사이클1 규칙)") + void 사_목적지에_적군_존재시_이동_가능() { + //given + Board board = new Board(); + Position position = new Position(4, 10); + board.place(position, new Piece(Team.CHO, PieceType.SA)); + board.place(new Position(5, 9), new Piece(Team.HAN, PieceType.CHA)); + Position maPos1 = new Position(4, 9); + Position maPos2 = new Position(5, 10); + Position maPos3 = new Position(3, 9); + Position maPos4 = new Position(3, 10); + Position maPos5 = new Position(5, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕은 이동경로에 장애물이 없는 곳으로 이동할 수 있다 (사이클1 규칙)") + void 왕_장애물_없을때_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(5, 9); + board.place(position, new Piece(Team.CHO, PieceType.KING)); + Position maPos1 = new Position(4, 8); + Position maPos2 = new Position(5, 8); + Position maPos3 = new Position(6, 8); + Position maPos4 = new Position(6, 9); + Position maPos5 = new Position(6, 10); + Position maPos6 = new Position(5, 10); + Position maPos7 = new Position(4, 10); + Position maPos8 = new Position(4, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7, maPos8); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕의 이동하려는 목적지에 아군 기물이 있으면 이동할 수 없다(사이클1 규칙)") + void 왕_목적지에_아군_존재시_이동_불가() { + //given + Board board = new Board(); + Position position = new Position(5, 9); + board.place(position, new Piece(Team.CHO, PieceType.KING)); + board.place(new Position(4, 9), new Piece(Team.CHO, PieceType.SA)); + board.place(new Position(4, 10), new Piece(Team.CHO, PieceType.SA)); + Position maPos1 = new Position(4, 8); + Position maPos2 = new Position(5, 8); + Position maPos3 = new Position(6, 8); + Position maPos4 = new Position(6, 9); + Position maPos5 = new Position(6, 10); + Position maPos6 = new Position(5, 10); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕의 목적지에 적군 기물이 있으면 이동할 수 있다 (사이클1 규칙)") + void 왕_목적있지에_적군_존재시_이동_가능() { + //given + Board board = new Board(); + Position position = new Position(5, 9); + board.place(position, new Piece(Team.CHO, PieceType.KING)); + board.place(new Position(4, 9), new Piece(Team.HAN, PieceType.ZOL)); + board.place(new Position(4, 10), new Piece(Team.HAN, PieceType.CHA)); + Position maPos1 = new Position(4, 8); + Position maPos2 = new Position(5, 8); + Position maPos3 = new Position(6, 8); + Position maPos4 = new Position(6, 9); + Position maPos5 = new Position(6, 10); + Position maPos6 = new Position(5, 10); + Position maPos7 = new Position(4, 10); + Position maPos8 = new Position(4, 9); + List rightAnswer = List.of(maPos1, maPos2, maPos3, maPos4, maPos5, maPos6, maPos7, maPos8); + + //when + List maRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 장애물이 없으면 끝까지 이동할 수 있다") + void 차_장애물_없을때_직선_끝까지_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(1, 10); + board.place(position, new Piece(Team.CHO, PieceType.CHA)); + List upRoutes = List.of( + new Position(1, 9), new Position(1, 8), new Position(1, 7), + new Position(1, 6), new Position(1, 5), new Position(1, 4), + new Position(1, 3), new Position(1, 2), new Position(1, 1) + ); + + List rightRoutes = List.of( + new Position(2, 10), new Position(3, 10), new Position(4, 10), + new Position(5, 10), new Position(6, 10), new Position(7, 10), + new Position(8, 10), new Position(9, 10) + ); + List rightAnswer = new ArrayList<>(); + rightAnswer.addAll(upRoutes); + rightAnswer.addAll(rightRoutes); + + //when + List chaRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(chaRoutesPositions).hasSize(17) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 아군 기물이 있으면 아군 기물 직전까지만 이동할 수 있다") + void 차_경로에_아군_존재시_아군_직전까지_이동_가능() { + //given + Board board = new Board(); + Position position = new Position(1, 10); + board.place(position, new Piece(Team.CHO, PieceType.CHA)); + board.place(new Position(1, 7), new Piece(Team.CHO, PieceType.ZOL)); + List upRoutes = List.of( + new Position(1, 9), new Position(1, 8) + ); + + List rightRoutes = List.of( + new Position(2, 10), new Position(3, 10), new Position(4, 10), + new Position(5, 10), new Position(6, 10), new Position(7, 10), + new Position(8, 10), new Position(9, 10) + ); + List rightAnswer = new ArrayList<>(); + rightAnswer.addAll(upRoutes); + rightAnswer.addAll(rightRoutes); + + //when + List chaRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(chaRoutesPositions).hasSize(10) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 적군 기물이 있으면 포획할 수 있는 적군 기물 위치까지만 이동할 수 있다") + void 차_경로에_적군_존재시_적군_위치까지_이동_가능() { + //given + Board board = new Board(); + Position position = new Position(1, 10); + board.place(position, new Piece(Team.CHO, PieceType.CHA)); + board.place(new Position(1, 7), new Piece(Team.HAN, PieceType.CHA)); + List upRoutes = List.of( + new Position(1, 9), new Position(1, 8), new Position(1, 7) + ); + + List rightRoutes = List.of( + new Position(2, 10), new Position(3, 10), new Position(4, 10), + new Position(5, 10), new Position(6, 10), new Position(7, 10), + new Position(8, 10), new Position(9, 10) + ); + List rightAnswer = new ArrayList<>(); + rightAnswer.addAll(upRoutes); + rightAnswer.addAll(rightRoutes); + + //when + List chaRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(chaRoutesPositions).hasSize(11) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("포는 이동 경로상에 일반 기물(포다리)이 딱 1개 존재하면 그 너머로 이동할 수 있다") + void 포_경로에_일반_기물_포다리가_1개_있을때_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(5, 8); + board.place(position, new Piece(Team.CHO, PieceType.PO)); + board.place(new Position(5, 6), new Piece(Team.HAN, PieceType.CHA)); + List upRoutes = List.of( + new Position(5, 5), new Position(5, 4), new Position(5, 3), + new Position(5, 2), new Position(5, 1) + ); + + List rightAnswer = new ArrayList<>(upRoutes); + + //when + List chaRoutesPositions = routeChecker.findAvailablePositions(board, position); + + //then + assertThat(chaRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("포는 넘어가려는 목적지에 또 다른 포가 있으면, 포는 포를 포획할 수 없으므로 이동할 수 없다") + void 포_목적지에_다른_포가_있으면_포획_및_이동_불가() { + Board board = new Board(); + Position position = new Position(5, 8); + board.place(position, new Piece(Team.CHO, PieceType.PO)); + board.place(new Position(5, 6), new Piece(Team.HAN, PieceType.CHA)); + board.place(new Position(5, 5), new Piece(Team.HAN, PieceType.PO)); + List upRoutes = List.of(); + + List rightAnswer = new ArrayList<>(upRoutes); + + assertThrows(IllegalArgumentException.class, () -> { + routeChecker.findAvailablePositions(board, position); + }); + } + + @Test + @DisplayName("포는 포다리 너머에 일반 적군 기물이 존재하면 해당 기물을 포획하며 이동할 수 있다") + void 포_목적지에_일반_적군_기물이_있을때_이동_성공() { + //given + Board board = new Board(); + Position position = new Position(5, 8); + board.place(position, new Piece(Team.CHO, PieceType.PO)); + board.place(new Position(5, 6), new Piece(Team.HAN, PieceType.CHA)); + board.place(new Position(5, 5), new Piece(Team.HAN, PieceType.ZOL)); + List upRoutes = List.of(new Position(5, 5)); + + List rightAnswer = new ArrayList<>(upRoutes); + + //when + List chaRoutesPositions = routeChecker.findAvailablePositions(board, position); + //then + assertThat(chaRoutesPositions).hasSize(1) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } +} diff --git a/src/test/java/janggi/domain/route/RouteConverterTest.java b/src/test/java/janggi/domain/route/RouteConverterTest.java new file mode 100644 index 0000000000..baa05b4272 --- /dev/null +++ b/src/test/java/janggi/domain/route/RouteConverterTest.java @@ -0,0 +1,195 @@ +package janggi.domain.route; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.board.Board; +import janggi.domain.common.Position; +import janggi.domain.common.Team; +import janggi.domain.piece.Piece; +import janggi.domain.piece.PieceType; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RouteConverterTest { + RouteConverter routeConverter = new RouteConverter(); + + @Test + @DisplayName("초나라 졸의 왼쪽 방향이 보드 범위를 넘어서 불가능한 좌표이다") + void 초나라_졸_보드_범위_내_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(1, 5); + Piece piece = new Piece(Team.CHO, PieceType.ZOL); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(1, 4), new Position(2, 5) + ); + } + + @Test + @DisplayName("초나라 졸의 세 방향 모두 보드 범위 내에 있다") + void 초나라_졸_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(2, 5); + Piece piece = new Piece(Team.CHO, PieceType.ZOL); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(1, 5), new Position(2, 4), new Position(3, 5) + ); + } + + @Test + @DisplayName("한나라 졸의 세 방향 모두 보드 범위 내에 있다") + void 한나라_졸_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(3, 4); + Piece piece = new Piece(Team.HAN, PieceType.ZOL); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(3, 5), new Position(2, 4), new Position(4, 4) + ); + } + + @Test + @DisplayName("마의 8방향 모두 보드 범위 내에 있다") + void 마_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(4, 6); + Piece piece = new Piece(Team.CHO, PieceType.MA); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(3, 4), new Position(5, 4), new Position(6, 5), new Position(6, 7), + new Position(3, 8), new Position(5, 8), new Position(2, 5), new Position(2, 7) + ); + } + + @Test + @DisplayName("상의 8방향 모두 보드 범위 내에 있다") + void 상_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(5, 7); + Piece piece = new Piece(Team.CHO, PieceType.SANG); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(3, 4), new Position(7, 4), new Position(8, 5), new Position(8, 9), + new Position(3, 10), new Position(7, 10), new Position(2, 5), new Position(2, 9) + ); + } + + @Test + @DisplayName("사의 8방향 모두 보드 범위 내에 있다") + void 사_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(5, 9); + Piece piece = new Piece(Team.CHO, PieceType.SA); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(5, 8), new Position(6, 9), new Position(5, 10), new Position(4, 9), + new Position(4, 8), new Position(6, 8), new Position(6, 10), new Position(4, 10) + ); + } + + @Test + @DisplayName("왕의 8방향 모두 보드 범위 내에 있다") + void 왕_모든_경로_가능() { + // given + Board board = new Board(); + Position position = new Position(5, 9); + Piece piece = new Piece(Team.CHO, PieceType.KING); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(5, 8), new Position(6, 9), new Position(5, 10), new Position(4, 9), + new Position(4, 8), new Position(6, 8), new Position(6, 10), new Position(4, 10) + ); + } + + @Test + @DisplayName("차는 현재 위치에서 보드 범위 내에서 상하좌우로 계속 갈 수 있다") + void 차_보드_범위_내_상하좌우_계속_가능() { + // given + Board board = new Board(); + Position position = new Position(2, 8); + Piece piece = new Piece(Team.HAN, PieceType.CHA); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(2, 1), new Position(2, 2), new Position(2, 3), new Position(2, 4), + new Position(2, 5), new Position(2, 6), new Position(2, 7), + new Position(2, 9), new Position(2, 10), + + new Position(1, 8), + new Position(3, 8), new Position(4, 8), new Position(5, 8), new Position(6, 8), + new Position(7, 8), new Position(8, 8), new Position(9, 8) + ); + } + + @Test + @DisplayName("포는 현재 위치에서 보드 범위 내에서 상하좌우로 계속 갈 수 있다") + void 포_보드_범위_내_상하좌우_계속_가능() { + // given + Board board = new Board(); + Position position = new Position(2, 8); + Piece piece = new Piece(Team.HAN, PieceType.PO); + board.place(position, piece); + + // when + Map> result = routeConverter.convertToPosition(board, position); + + // then + assertThat(result.keySet()).containsExactlyInAnyOrder( + new Position(2, 1), new Position(2, 2), new Position(2, 3), new Position(2, 4), + new Position(2, 5), new Position(2, 6), new Position(2, 7), + new Position(2, 9), new Position(2, 10), + + new Position(1, 8), + new Position(3, 8), new Position(4, 8), new Position(5, 8), new Position(6, 8), + new Position(7, 8), new Position(8, 8), new Position(9, 8) + ); + } +}