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/.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..1fd259dd3d --- /dev/null +++ b/src/main/java/janggi/Application.java @@ -0,0 +1,10 @@ +package janggi; + +import janggi.controller.JanggiGame; + +public class Application { + public static void main(String[] args) { + JanggiGame janggiGame = new JanggiGame(); + janggiGame.run(); + } +} diff --git a/src/main/java/janggi/controller/JanggiGame.java b/src/main/java/janggi/controller/JanggiGame.java new file mode 100644 index 0000000000..71b954d756 --- /dev/null +++ b/src/main/java/janggi/controller/JanggiGame.java @@ -0,0 +1,71 @@ +package janggi.controller; + +import janggi.domain.Piece; +import janggi.domain.Position; +import janggi.domain.board.Board; +import janggi.domain.board.BoardFactory; +import janggi.view.InputView; +import janggi.view.OutputView; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class JanggiGame { + + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + private boolean isChoTurn = true; + + public void run() { + Board board = initializeBoard(); + outputView.printBoard(board.getBoard()); + while (true) { + isChoTurn = playTurn(board); + } + } + + private boolean playTurn(Board board) { + outputView.printTurnMessage(isChoTurn); + Position movePiecePosition = choosePieceToMove(board); + List availablePositions = board.findAvailablePositions(movePiecePosition); + outputView.printAvailablePositions(board.getBoard(), availablePositions); + Position targetPosition = chooseTargetPosition(board, movePiecePosition); + board.movePiece(movePiecePosition, targetPosition); + outputView.printBoard(board.getBoard()); + isChoTurn = !isChoTurn; + return isChoTurn; + } + + private Board initializeBoard() { + Map initBoard = BoardFactory.settingUpBoard(); + return new Board(initBoard); + } + + private Position chooseTargetPosition(Board board, Position movePiecePosition) { + return retry(() -> { + outputView.printMoveChoiceInfo(); + Position position = inputView.readPosition(); + board.validateDestination(movePiecePosition, position); + return position; + }); + } + + private Position choosePieceToMove(Board board) { + return retry(() -> { + outputView.printMoveInfo(); + Position position = inputView.readPosition(); + board.findAvailablePositions(position); + return position; + }); + } + + private T retry(Supplier inputFunction) { + while (true) { + try { + return inputFunction.get(); + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/janggi/domain/Direction.java b/src/main/java/janggi/domain/Direction.java new file mode 100644 index 0000000000..0184cacb3d --- /dev/null +++ b/src/main/java/janggi/domain/Direction.java @@ -0,0 +1,29 @@ +package janggi.domain; + +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 int getX() { + return x; + } + + public int getY() { + return y; + } +} diff --git a/src/main/java/janggi/domain/MoveRule.java b/src/main/java/janggi/domain/MoveRule.java new file mode 100644 index 0000000000..3337cf6c6e --- /dev/null +++ b/src/main/java/janggi/domain/MoveRule.java @@ -0,0 +1,7 @@ +package janggi.domain; + +import java.util.List; + +public interface MoveRule { + List findRoutes(Team team); +} diff --git a/src/main/java/janggi/domain/Piece.java b/src/main/java/janggi/domain/Piece.java new file mode 100644 index 0000000000..742ee4d44e --- /dev/null +++ b/src/main/java/janggi/domain/Piece.java @@ -0,0 +1,48 @@ +package janggi.domain; + +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 PieceType getPieceType() { + return pieceType; + } + + public Team getTeam() { + return team; + } + + public boolean isCha() { + return pieceType == PieceType.CHA; + } + + public boolean isPo() { + return pieceType == PieceType.PO; + } + + public List findRoutes() { + return pieceType.getMoveRule().findRoutes(this.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/PieceType.java b/src/main/java/janggi/domain/PieceType.java new file mode 100644 index 0000000000..6e18835e9e --- /dev/null +++ b/src/main/java/janggi/domain/PieceType.java @@ -0,0 +1,35 @@ +package janggi.domain; + +import janggi.domain.moveRules.ChaMoveRule; +import janggi.domain.moveRules.KingMoveRule; +import janggi.domain.moveRules.MaMoveRule; +import janggi.domain.moveRules.PoMoveRule; +import janggi.domain.moveRules.SaMoveRule; +import janggi.domain.moveRules.SangMoveRule; +import janggi.domain.moveRules.ZolMoveRule; + +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 MoveRule getMoveRule() { + return moveRule; + } +} diff --git a/src/main/java/janggi/domain/Position.java b/src/main/java/janggi/domain/Position.java new file mode 100644 index 0000000000..9aa38b6195 --- /dev/null +++ b/src/main/java/janggi/domain/Position.java @@ -0,0 +1,51 @@ +package janggi.domain; + +import java.util.Objects; + +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 static boolean isInsideBoundary(int x, int y) { + return x >= MIN_X && x <= MAX_X && y >= MIN_Y && y <= MAX_Y; + } + + public int getX() { + return x; + } + + public int getY() { + return 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); + } + + private void validateBoundary(int x, int y) { + if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) { + throw new IllegalArgumentException("[ERROR] 보드 범위를 벗어났습니다."); + } + } +} diff --git a/src/main/java/janggi/domain/Route.java b/src/main/java/janggi/domain/Route.java new file mode 100644 index 0000000000..42587bbe3a --- /dev/null +++ b/src/main/java/janggi/domain/Route.java @@ -0,0 +1,30 @@ +package janggi.domain; + +import java.util.List; +import java.util.Objects; + +public class Route { + + private final List routes; + + public Route(List routes) { + this.routes = routes; + } + + public List getRoutes() { + return routes; + } + + @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/Team.java b/src/main/java/janggi/domain/Team.java new file mode 100644 index 0000000000..ed86b28d23 --- /dev/null +++ b/src/main/java/janggi/domain/Team.java @@ -0,0 +1,7 @@ +package janggi.domain; + +public enum Team { + CHO, + HAN, + ; +} 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..b9466917c1 --- /dev/null +++ b/src/main/java/janggi/domain/board/Board.java @@ -0,0 +1,188 @@ +package janggi.domain.board; + +import janggi.domain.Direction; +import janggi.domain.Piece; +import janggi.domain.PieceType; +import janggi.domain.Position; +import janggi.domain.Route; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Board { + + private final Map board; + + public Board(Map initBoard) { + this.board = initBoard; + } + + public Map getBoard() { + return board; + } + + public List findAvailablePositions(Position position) { + Piece piece = board.get(position); + List routes = piece.findRoutes(); + Map> routePositions = convertToPositions(position, piece, routes); + + List availablePositions = new ArrayList<>(); + for (Map.Entry> entry : routePositions.entrySet()) { + Position destination = entry.getKey(); + List route = entry.getValue(); + if (canPieceMove(piece, destination, route)) { + availablePositions.add(destination); + } + } + validateCantMovePiece(availablePositions); + return availablePositions; + } + + + private boolean canPieceMove(Piece piece, Position destination, List route) { + Piece destinationPiece = board.get(destination); + if (piece.isPo()) { + return canPoMove(piece, destinationPiece, route); + } + return canGeneralPieceMove(piece, destinationPiece, route); + } + + private boolean canPoMove(Piece piece, Piece destinationPiece, List route) { + if (!hasOneObstacleAndNotPo(route)) { + return false; + } + if (destinationPiece == null) { + return true; + } + return isDestinationIsEnemy(destinationPiece, piece) && !isDestinationIsPo(destinationPiece); + } + + private boolean canGeneralPieceMove(Piece piece, Piece destinationPiece, List route) { + if (hasObstacleOnRoute(route)) { + return false; + } + if (destinationPiece == null) { + return true; + } + return isDestinationIsEnemy(destinationPiece, piece); + } + + private boolean isDestinationIsPo(Piece destinationPiece) { + return destinationPiece.getPieceType() == PieceType.PO; + } + + private boolean isDestinationIsEnemy(Piece destinationPiece, Piece piece) { + return destinationPiece.getTeam() != piece.getTeam(); + } + + private boolean hasObstacleOnRoute(List route) { + Position targetPosition = route.getLast(); + return route.stream() + .filter(position -> position != targetPosition) + .anyMatch(board::containsKey); + } + + private Map> convertToPositions(Position position, Piece piece, List routes) { + Map> result = new HashMap<>(); + + if (piece.isCha() || piece.isPo()) { + return convertToContinuousRoutes(position, routes, result); + } + + return convertToFixedRoutes(position, routes, result); + } + + private Map> convertToFixedRoutes(Position position, List routes, + Map> result) { + for (Route route : routes) { + addValidFixedRoute(position, result, route); + } + return result; + } + + private static void addValidFixedRoute(Position position, Map> result, Route route) { + int currentX = position.getX(); + int currentY = position.getY(); + List routeToPositions = new ArrayList<>(); + + for (Direction direction : route.getRoutes()) { + currentX += direction.getX(); + currentY += direction.getY(); + if (!Position.isInsideBoundary(currentX, currentY)) { + return; + } + routeToPositions.add(new Position(currentX, currentY)); + } + if (!routeToPositions.isEmpty()) { + result.put(routeToPositions.getLast(), routeToPositions); + } + } + + private Map> convertToContinuousRoutes(Position position, List routes, + Map> result) { + for (Route route : routes) { + addValidContinuousPosition(position, result, route); + } + return result; + } + + private static void addValidContinuousPosition(Position position, Map> result, + Route route) { + int currentX = position.getX(); + int currentY = position.getY(); + List directions = route.getRoutes(); + + for (Direction direction : directions) { + List routeToPositions = new ArrayList<>(); + currentX += direction.getX(); + currentY += direction.getY(); + while (Position.isInsideBoundary(currentX, currentY)) { + Position movePosition = new Position(currentX, currentY); + routeToPositions.add(movePosition); + result.put(movePosition, new ArrayList<>(routeToPositions)); + currentX += direction.getX(); + currentY += direction.getY(); + } + } + } + + private boolean hasOneObstacleAndNotPo(List route) { + int count = 0; + List obstacles = new ArrayList<>(); + for (int i = 0; i < route.size() - 1; i++) { + if (board.containsKey(route.get(i))) { + count++; + obstacles.add(board.get(route.get(i))); + } + } + return count == 1 && obstacles.getFirst().getPieceType() != PieceType.PO; + } + + public void movePiece(Position movePiecePosition, Position destination) { + Piece piece = board.get(movePiecePosition); + board.remove(movePiecePosition); + board.put(destination, piece); + } + + public void validateDestination(Position movePiecePosition, Position destination) { + List availablePositions = findAvailablePositions(movePiecePosition); + boolean hasPosition = false; + for (Position position : availablePositions) { + if (position.equals(destination)) { + hasPosition = true; + break; + } + } + + if (!hasPosition) { + throw new IllegalArgumentException("[ERROR] 이동 가능한 좌표 중에서 선택하세요."); + } + } + + private static void validateCantMovePiece(List availablePositions) { + if (availablePositions.isEmpty()) { + throw new IllegalArgumentException("[ERROR] 이동할 수 없는 좌표입니다."); + } + } +} diff --git a/src/main/java/janggi/domain/board/BoardFactory.java b/src/main/java/janggi/domain/board/BoardFactory.java new file mode 100644 index 0000000000..5c6bf2cb65 --- /dev/null +++ b/src/main/java/janggi/domain/board/BoardFactory.java @@ -0,0 +1,107 @@ +package janggi.domain.board; + +import janggi.domain.Piece; +import janggi.domain.PieceType; +import janggi.domain.Position; +import janggi.domain.Team; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BoardFactory { + + public static Map settingUpBoard() { + Map board = new HashMap<>(); + settingUpZol(board); + settingUpPo(board); + settingUpCha(board); + settingUpMa(board); + settingUpSang(board); + settingUpSa(board); + settingUpKing(board); + return board; + } + + + private static void settingUpZol(Map board) { + final int CHO_ZOL_Y = 7; + final int HAN_ZOL_Y = 4; + + List zolPositions = List.of(1, 3, 5, 7, 9); + + for (int x : zolPositions) { + board.put(new Position(x, CHO_ZOL_Y), new Piece(Team.CHO, PieceType.ZOL)); + board.put(new Position(x, HAN_ZOL_Y), new Piece(Team.HAN, PieceType.ZOL)); + } + } + + private static void settingUpPo(Map board) { + final int CHO_PO_Y = 8; + final int HAN_PO_Y = 3; + + List poPositions = List.of(2, 8); + + for (int x : poPositions) { + board.put(new Position(x, CHO_PO_Y), new Piece(Team.CHO, PieceType.PO)); + board.put(new Position(x, HAN_PO_Y), new Piece(Team.HAN, PieceType.PO)); + } + } + + private static void settingUpCha(Map board) { + final int CHO_CHA_Y = 10; + final int HAN_CHA_Y = 1; + + List chaPositions = List.of(1, 9); + + for (int x : chaPositions) { + board.put(new Position(x, CHO_CHA_Y), new Piece(Team.CHO, PieceType.CHA)); + board.put(new Position(x, HAN_CHA_Y), new Piece(Team.HAN, PieceType.CHA)); + } + } + + private static void settingUpMa(Map board) { + final int CHO_MA_Y = 10; + final int HAN_MA_Y = 1; + + List chaPositions = List.of(2, 7); + + for (int x : chaPositions) { + board.put(new Position(x, CHO_MA_Y), new Piece(Team.CHO, PieceType.MA)); + board.put(new Position(x, HAN_MA_Y), new Piece(Team.HAN, PieceType.MA)); + } + } + + private static void settingUpSang(Map board) { + final int CHO_SANG_Y = 10; + final int HAN_SANG_Y = 1; + + List chaPositions = List.of(3, 8); + + for (int x : chaPositions) { + board.put(new Position(x, CHO_SANG_Y), new Piece(Team.CHO, PieceType.SANG)); + board.put(new Position(x, HAN_SANG_Y), new Piece(Team.HAN, PieceType.SANG)); + } + } + + private static void settingUpSa(Map board) { + final int CHO_SA_Y = 10; + final int HAN_SA_Y = 1; + + List chaPositions = List.of(4, 6); + + for (int x : chaPositions) { + board.put(new Position(x, CHO_SA_Y), new Piece(Team.CHO, PieceType.SA)); + board.put(new Position(x, HAN_SA_Y), new Piece(Team.HAN, PieceType.SA)); + } + } + + private static void settingUpKing(Map board) { + final int CHO_KING_X = 5; + final int CHO_KING_Y = 9; + final int HAN_KING_X = 5; + final int HAN_KING_Y = 2; + + board.put(new Position(CHO_KING_X, CHO_KING_Y), new Piece(Team.CHO, PieceType.KING)); + board.put(new Position(HAN_KING_X, HAN_KING_Y), new Piece(Team.HAN, PieceType.KING)); + } +} diff --git a/src/main/java/janggi/domain/moveRules/ChaMoveRule.java b/src/main/java/janggi/domain/moveRules/ChaMoveRule.java new file mode 100644 index 0000000000..39993bd48f --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/ChaMoveRule.java @@ -0,0 +1,19 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/KingMoveRule.java b/src/main/java/janggi/domain/moveRules/KingMoveRule.java new file mode 100644 index 0000000000..d0174ddb44 --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/KingMoveRule.java @@ -0,0 +1,23 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/MaMoveRule.java b/src/main/java/janggi/domain/moveRules/MaMoveRule.java new file mode 100644 index 0000000000..99379b9dcf --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/MaMoveRule.java @@ -0,0 +1,23 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/PoMoveRule.java b/src/main/java/janggi/domain/moveRules/PoMoveRule.java new file mode 100644 index 0000000000..ba61b20f85 --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/PoMoveRule.java @@ -0,0 +1,19 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/SaMoveRule.java b/src/main/java/janggi/domain/moveRules/SaMoveRule.java new file mode 100644 index 0000000000..75a1892952 --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/SaMoveRule.java @@ -0,0 +1,23 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/SangMoveRule.java b/src/main/java/janggi/domain/moveRules/SangMoveRule.java new file mode 100644 index 0000000000..fbc492bcf9 --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/SangMoveRule.java @@ -0,0 +1,23 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/moveRules/ZolMoveRule.java b/src/main/java/janggi/domain/moveRules/ZolMoveRule.java new file mode 100644 index 0000000000..d1c86f04f2 --- /dev/null +++ b/src/main/java/janggi/domain/moveRules/ZolMoveRule.java @@ -0,0 +1,24 @@ +package janggi.domain.moveRules; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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/view/InputView.java b/src/main/java/janggi/view/InputView.java new file mode 100644 index 0000000000..52f67e5ef6 --- /dev/null +++ b/src/main/java/janggi/view/InputView.java @@ -0,0 +1,16 @@ +package janggi.view; + +import janggi.domain.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])); + } +} diff --git a/src/main/java/janggi/view/OutputView.java b/src/main/java/janggi/view/OutputView.java new file mode 100644 index 0000000000..e7cff6fcd1 --- /dev/null +++ b/src/main/java/janggi/view/OutputView.java @@ -0,0 +1,109 @@ +package janggi.view; + +import janggi.domain.Piece; +import janggi.domain.PieceType; +import janggi.domain.Position; +import janggi.domain.Team; +import java.util.Collections; +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"}; + private static final String EMPTY_MARK = ". "; + private static final String AVAILABLE_MARK = "O "; + + public static void printErrorMessage(String message) { + System.out.printf("%s%n", message); + } + + public void printBoard(Map board) { + printGrid(board, Collections.emptyList()); + } + + public void printAvailablePositions(Map board, List availablePositions) { + printGrid(board, availablePositions); + } + + private void printGrid(Map board, List availablePositions) { + printXAxis(); + + for (int y = 1; y <= 10; y++) { + System.out.printf("%2d ", y); + for (int x = 1; x <= 9; x++) { + Position currentPos = new Position(x, y); + printCell(board, availablePositions, currentPos); + } + System.out.println(); + } + } + + private void printXAxis() { + System.out.print(" "); + for (String xValue : X_VALUES) { + System.out.print(xValue + " "); + } + System.out.println(); + } + + private void printCell(Map board, List availablePositions, Position position) { + boolean isAvailable = availablePositions.contains(position); + boolean hasPiece = board.containsKey(position); + + if (isAvailable && hasPiece) { + Piece piece = board.get(position); + String pieceName = resolvePieceName(piece.getPieceType()); + System.out.print(AVAILABLE_COLOR + pieceName + RESET + " "); + } else if (isAvailable) { + System.out.print(AVAILABLE_COLOR + AVAILABLE_MARK + RESET); + } else if (hasPiece) { + Piece piece = board.get(position); + String pieceName = resolvePieceName(piece.getPieceType()); + String color = getColorByTeam(piece.getTeam()); + System.out.print(color + pieceName + RESET + " "); + } else { + System.out.print(EMPTY_MARK); + } + } + + private String resolvePieceName(PieceType pieceType) { + return switch (pieceType) { + case KING -> "왕"; + case SA -> "사"; + case SANG -> "상"; + case MA -> "마"; + case CHA -> "차"; + case PO -> "포"; + case ZOL -> "졸"; + }; + } + + private String getColorByTeam(Team team) { + if (team == Team.CHO) { + return CHO_COLOR; + } + return HAN_COLOR; + } + + public void printTurnMessage(boolean isChoTurn) { + if (isChoTurn) { + System.out.println(CHO_COLOR + "\n현재 초나라 차례입니다" + RESET); + } else { + System.out.println(HAN_COLOR + "\n한나라 차례입니다" + RESET); + } + } + + public void printMoveInfo() { + System.out.println("이동하고 싶은 기물의 좌표를 입력하세요."); + } + + public void printMoveChoiceInfo() { + System.out.println("이동하고자 하는 목표 지점의 좌표를 입력하세요."); + } +} 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/BoardFactoryTest.java b/src/test/java/janggi/domain/BoardFactoryTest.java new file mode 100644 index 0000000000..ff11f7103a --- /dev/null +++ b/src/test/java/janggi/domain/BoardFactoryTest.java @@ -0,0 +1,46 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.board.Board; +import janggi.domain.board.BoardFactory; +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 BoardFactoryTest { + @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 + Board board = new Board(BoardFactory.settingUpBoard()); + Position position = new Position(x, y); + Piece piece = new Piece(team, pieceType); + + //when + Map checkPiece = board.getBoard(); + + //then + assertThat(checkPiece.get(position)).isEqualTo(piece); + } +} diff --git a/src/test/java/janggi/domain/BoardTest.java b/src/test/java/janggi/domain/BoardTest.java new file mode 100644 index 0000000000..3e1d7d6c7c --- /dev/null +++ b/src/test/java/janggi/domain/BoardTest.java @@ -0,0 +1,541 @@ +package janggi.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import janggi.domain.board.Board; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +public class BoardTest { + + @Test + @DisplayName("초나라 졸은 경로에 장애물이 없으면 위쪽, 왼쪽, 오른쪽으로 이동할 수 있다") + void 초나라_졸_장애물_없을때_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(rightAnswer).isEqualTo(zolRoutesPositions); + } + + @Test + @DisplayName("초나라 졸의 이동 방향에 아군 기물이 있으면 해당 방향으로는 이동할 수 없다") + void 초나라_졸_아군이_막고있을때_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.ZOL)); + Position zolUp = new Position(5, 6); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(rightAnswer).isEqualTo(zolRoutesPositions); + } + + @Test + @DisplayName("마는 이동 경로(멱)에 장애물이 없으면 8방향 모두 이동할 수 있다") + void 마_장애물_없을때_8방향_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 6); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("마는 이동 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 마_멱이_막혀있을때_해당_방향_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 6); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.MA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("마의 최종 목적지에 아군 기물이 있으면 해당 좌표로 이동할 수 없다") + void 마_목적지에_아군_존재시_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 6); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.MA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 이동 경로(멱)에 장애물이 없으면 8방향 모두 이동할 수 있다") + void 상_장애물_없을때_8방향_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(sangRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 대각선 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 상_대각선_멱이_막혀있을때_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.SANG)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(sangRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상은 직선 경로(멱)에 다른 기물이 있으면 해당 방향으로 이동할 수 없다") + void 상_직선_멱이_막혀있을때_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.SANG)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(sangRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("상의 최종 목적지에 아군 기물이 있으면 해당 좌표로 이동할 수 없다") + void 상_목적지에_아군_존재시_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 7); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.SANG)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(sangRoutesPositions).hasSize(7) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사는 이동경로에 장애물이 없는 곳으로 이동할 수 있다 (사이클1 규칙)") + void 사_장애물_없을때_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 10); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사의 목적지에 아군 기물이 있으면 이동할 수 없다 (사이클1 규칙)") + void 사_목적지에_아군_존재시_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 10); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.SA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(4) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("사의 목적지에 적군 기물이 있으면 이동할 수 있다 (사이클1 규칙)") + void 사_목적지에_적군_존재시_이동_가능() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(4, 10); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.SA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕은 이동경로에 장애물이 없는 곳으로 이동할 수 있다 (사이클1 규칙)") + void 왕_장애물_없을때_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 9); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕의 이동하려는 목적지에 아군 기물이 있으면 이동할 수 없다(사이클1 규칙)") + void 왕_목적지에_아군_존재시_이동_불가() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 9); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.KING)); + board.getBoard().put(new Position(4, 9), new Piece(Team.CHO, PieceType.SA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(6) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("왕의 목적지에 적군 기물이 있으면 이동할 수 있다 (사이클1 규칙)") + void 왕_목적있지에_적군_존재시_이동_가능() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 9); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.KING)); + board.getBoard().put(new Position(4, 9), new Piece(Team.HAN, PieceType.ZOL)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(maRoutesPositions).hasSize(8) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 장애물이 없으면 끝까지 이동할 수 있다") + void 차_장애물_없을때_직선_끝까지_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(1, 10); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(chaRoutesPositions).hasSize(17) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 아군 기물이 있으면 아군 기물 직전까지만 이동할 수 있다") + void 차_경로에_아군_존재시_아군_직전까지_이동_가능() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(1, 10); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.CHA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(chaRoutesPositions).hasSize(10) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("차는 직선 경로상에 적군 기물이 있으면 포획할 수 있는 적군 기물 위치까지만 이동할 수 있다") + void 차_경로에_적군_존재시_적군_위치까지_이동_가능() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(1, 10); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.CHA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(chaRoutesPositions).hasSize(11) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("포는 이동 경로상에 일반 기물(포다리)이 딱 1개 존재하면 그 너머로 이동할 수 있다") + void 포_경로에_일반_기물_포다리가_1개_있을때_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 8); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.PO)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(chaRoutesPositions).hasSize(5) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } + + @Test + @DisplayName("포는 넘어가려는 목적지에 또 다른 포가 있으면, 포는 포를 포획할 수 없으므로 이동할 수 없다") + void 포_목적지에_다른_포가_있으면_포획_및_이동_불가() { + //given + Map zeroBoard = new HashMap<>(); + Board board = new Board(zeroBoard); + Position position = new Position(5, 8); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.PO)); + board.getBoard().put(new Position(5, 6), new Piece(Team.HAN, PieceType.CHA)); + board.getBoard().put(new Position(5, 5), new Piece(Team.HAN, PieceType.PO)); + + //when & then + assertThatThrownBy(() -> board.findAvailablePositions(position)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 이동할 수 없는 좌표입니다."); + } + + @Test + @DisplayName("포는 포다리 너머에 일반 적군 기물이 존재하면 해당 기물을 포획하며 이동할 수 있다") + void 포_목적지에_일반_적군_기물이_있을때_이동_성공() { + //given + Map emptyBoard = new HashMap<>(); + Board board = new Board(emptyBoard); + Position position = new Position(5, 8); + board.getBoard().put(position, new Piece(Team.CHO, PieceType.PO)); + board.getBoard().put(new Position(5, 6), new Piece(Team.HAN, PieceType.CHA)); + board.getBoard().put(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 = board.findAvailablePositions(position); + + //then + assertThat(chaRoutesPositions).hasSize(1) + .containsExactlyInAnyOrderElementsOf(rightAnswer); + } +} 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..5c52f61be0 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/ChaMoveRuleTest.java @@ -0,0 +1,33 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..eac266c091 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/KingMoveRuleTest.java @@ -0,0 +1,37 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..4564af4dd4 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/MaMoveRuleTest.java @@ -0,0 +1,37 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..15b35a0794 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/PoMoveRuleTest.java @@ -0,0 +1,33 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..8250d074eb --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/SaMoveRuleTest.java @@ -0,0 +1,37 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..ab54840159 --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/SangMoveRuleTest.java @@ -0,0 +1,37 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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..fc1dc9e7ff --- /dev/null +++ b/src/test/java/janggi/domain/moveRules/ZolMoveRuleTest.java @@ -0,0 +1,52 @@ +package janggi.domain.moveRules; + +import static org.assertj.core.api.Assertions.assertThat; + +import janggi.domain.Direction; +import janggi.domain.MoveRule; +import janggi.domain.Route; +import janggi.domain.Team; +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); + } +}