diff --git a/README.md b/README.md index 9775dda0ae..f9ad6209e3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,40 @@ # java-janggi -장기 미션 저장소 +# 기능 구현 목록 + +## 장기판 생성 +- [x] 9 * 10 구조의 장기판을 생성한다. + +## 기물 생성 +- [x] 기물을 생성한다. + - [x] 궁, 사, 마, 상, 차, 포, 졸(병) + - [x] None(장기판에 기물이 올라가있지 않은 상태)의 기물 + +## 보드 초기화 +- [x] 게임 시작시 장기판과 기물을 올바른 위치에 초기화한다. + - [x] 오른상(마상마상) 구조로 초기화한다. + - [x] 기물이 올바른 위치에 놓여있는지 확인한다. + +## 좌표 기본 이동 전략 +- [x] 기본적으로 좌표는 앞,뒤,좌,우,우상,좌상,우하,좌하로 움직일 수 있다. + - [x] 앞으로 이동하면 (-1,0)만큼 이동한다. + - [x] 뒤로 이동하면 (1,0)만큼 이동한다. + - [x] 좌로 이동하면 (0,-1)만큼 이동한다. + - [x] 우로 이동하면 (0,1)만큼 이동한다. + - [x] 우상으로 이동하면 (-1,1)만큼 이동한다. + - [x] 좌상으로 이동하면 (-1,-1)만큼 이동한다. + - [x] 우하로 이동하면 (1,1)만큼 이동한다. + - [x] 좌하로 이동하면 (1,-1)만큼 이동한다. + +## 기물 이동 전략 +- [x] 기물 이동 전략을 생성한다. + - [x] 궁,사 : 방향에 관계 없이 한 칸씩 이동할 수 있다. + - [x] 마 : 방향에 관계 없이 직진 후 대각선으로 한 칸 이동할 수 있다. + - [x] 이동하는 경로에 기물이 존재해서는 안된다. + - [x] 상 : 방향에 관계 없이 직진 후 대각선으로 두 칸 이동할 수 있다. + - [x] 이동하는 경로에 기물이 존재해서는 안된다. + - [x] 차 : 방향에 관계 없이 원하는만큼 이동할 수 있다. + - [x] 졸 : 좌,우,앞으로 한 칸씩 이동할 수 있다. + - [x] 뒤로는 갈 수 없다. + - [x] 포 : 앞에 기물이 한 개 존재할 때 원하는 곳으로 뛰어넘을 수 있다. + - [x] 포끼리는 뛰어넘을 수 없다. \ No newline at end of file diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000000..c4e80ce0a4 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,10 @@ +import controller.JanggiController; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + JanggiController janggiController = new JanggiController(new InputView(), new OutputView()); + janggiController.run(); + } +} diff --git a/src/main/java/controller/JanggiController.java b/src/main/java/controller/JanggiController.java new file mode 100644 index 0000000000..d6a8452dc0 --- /dev/null +++ b/src/main/java/controller/JanggiController.java @@ -0,0 +1,58 @@ +package controller; + +import domain.JanggiBoard; +import domain.JanggiBoardInitializer; +import domain.piece.Piece; +import domain.position.Position; +import view.InputView; +import view.OutputView; + +public class JanggiController { + private final InputView inputView; + private final OutputView outputView; + + public JanggiController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + JanggiBoard janggiBoard = new JanggiBoard(new JanggiBoardInitializer()); + while (true) { + try { + outputView.printBoard(janggiBoard); + Position from = inputMovePosition(); + Position to = inputTargetPosition(); + Piece currentPiece = janggiBoard.getPiece(from); + boolean movePiece = currentPiece.canMove(from, to, janggiBoard); + if (!movePiece) { + throw new IllegalArgumentException("[ERROR] 해당 위치로 이동할 수 없는 기물입니다."); + } + janggiBoard.move(from, to, currentPiece); + outputView.printBoard(janggiBoard); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e); + } + } + } + + private Position inputMovePosition() { + String inputMovePosition = inputView.inputMovePiece(); + String[] parts = inputMovePosition.split(","); + + int row = Integer.parseInt(parts[0].trim()); + int column = Integer.parseInt(parts[1].trim()); + + return new Position(row, column); + } + + private Position inputTargetPosition() { + String inputTargetPosition = inputView.inputTargetPosition(); + String[] parts = inputTargetPosition.split(","); + + int row = Integer.parseInt(parts[0].trim()); + int column = Integer.parseInt(parts[1].trim()); + + return new Position(row, column); + } +} diff --git a/src/main/java/domain/Index.java b/src/main/java/domain/Index.java new file mode 100644 index 0000000000..1754e498a0 --- /dev/null +++ b/src/main/java/domain/Index.java @@ -0,0 +1,17 @@ +package domain; + +public enum Index { + + BOARD_ROWS(10), + BOARD_COLUMNS(9); + + private final int index; + + Index(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } +} diff --git a/src/main/java/domain/JanggiBoard.java b/src/main/java/domain/JanggiBoard.java new file mode 100644 index 0000000000..24f4be206f --- /dev/null +++ b/src/main/java/domain/JanggiBoard.java @@ -0,0 +1,46 @@ +package domain; + +import static domain.Index.BOARD_COLUMNS; +import static domain.Index.BOARD_ROWS; + +import domain.piece.*; + +import domain.position.Position; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public class JanggiBoard implements PieceProvider { + + private final Map janggiBoard; + + public JanggiBoard(JanggiBoardInitializer initializer) { + this.janggiBoard = initializer.init(); + } + + public Map getJanggiBoard() { + return Collections.unmodifiableMap(janggiBoard); + } + + public void move(Position from, Position to, Piece currentPiece) { + janggiBoard.put(to, currentPiece); + janggiBoard.put(from, new Blank()); + } + + @Override + public boolean isBlank(Position position) { + Piece piece = janggiBoard.get(position); + return piece instanceof Blank; + } + + @Override + public boolean isCannon(Position position) { + Piece piece = janggiBoard.get(position); + return piece instanceof Cannon; + } + + @Override + public Piece getPiece(Position position) { + return janggiBoard.get(position); + } +} diff --git a/src/main/java/domain/JanggiBoardInitializer.java b/src/main/java/domain/JanggiBoardInitializer.java new file mode 100644 index 0000000000..ab37c17045 --- /dev/null +++ b/src/main/java/domain/JanggiBoardInitializer.java @@ -0,0 +1,60 @@ +package domain; + +import static domain.Index.BOARD_COLUMNS; +import static domain.Index.BOARD_ROWS; + +import domain.piece.Blank; +import domain.piece.Cannon; +import domain.piece.Car; +import domain.piece.Elephant; +import domain.piece.Guard; +import domain.piece.Horse; +import domain.piece.King; +import domain.piece.Pawn; +import domain.piece.Piece; +import domain.position.Position; +import java.util.HashMap; +import java.util.Map; + +public class JanggiBoardInitializer { + public Map init() { + Map boardSetting = new HashMap<>(); + + for (int row = 0; row < BOARD_ROWS.getIndex(); row++) { + for (int col = 0; col < BOARD_COLUMNS.getIndex(); col++) { + boardSetting.put(new Position(row, col), new Blank()); + } + } + + setupTeamPieces(boardSetting, Team.HAN, 0, 1, 2, 3); // 한나라 기물 배치 (0~3행 위주) + setupTeamPieces(boardSetting, Team.CHO, 9, 8, 7, 6); // 초나라 기물 배치 (9~6행 위주) + + return boardSetting; + } + + private void setupTeamPieces(Map boardSetting, Team team, int baseRow, int kingRow, int cannonRow, + int pawnRow) { + // 차 + boardSetting.put(new Position(baseRow, 0), new Car(team)); + boardSetting.put(new Position(baseRow, 8), new Car(team)); + // 마 + boardSetting.put(new Position(baseRow, 1), new Horse(team)); + boardSetting.put(new Position(baseRow, 6), new Horse(team)); + // 상 + boardSetting.put(new Position(baseRow, 2), new Elephant(team)); + boardSetting.put(new Position(baseRow, 7), new Elephant(team)); + // 사 + boardSetting.put(new Position(baseRow, 3), new Guard(team)); + boardSetting.put(new Position(baseRow, 5), new Guard(team)); + // 궁 + boardSetting.put(new Position(kingRow, 4), new King(team)); + // 포 + boardSetting.put(new Position(cannonRow, 1), new Cannon(team)); + boardSetting.put(new Position(cannonRow, 7), new Cannon(team)); + // 졸/병 + for (int col = 0; col < 9; col += 2) { + boardSetting.put(new Position(pawnRow, col), new Pawn(team)); + } + } + +} diff --git a/src/main/java/domain/JanggiGame.java b/src/main/java/domain/JanggiGame.java new file mode 100644 index 0000000000..1431776603 --- /dev/null +++ b/src/main/java/domain/JanggiGame.java @@ -0,0 +1,20 @@ +package domain; + +import domain.piece.Blank; +import domain.piece.Piece; +import domain.position.Position; + +public class JanggiGame { + + public void play(Position from, Position to, PieceProvider janggiBoard) { + Piece piece = janggiBoard.getPiece(from); + validateBlank(piece); + piece.canMove(from, to, janggiBoard); + } + + private void validateBlank(Piece piece) { + if (piece instanceof Blank) { + throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다."); + } + } +} diff --git a/src/main/java/domain/PieceProvider.java b/src/main/java/domain/PieceProvider.java new file mode 100644 index 0000000000..d087caa2f2 --- /dev/null +++ b/src/main/java/domain/PieceProvider.java @@ -0,0 +1,10 @@ +package domain; + +import domain.piece.Piece; +import domain.position.Position; + +public interface PieceProvider { + boolean isBlank(Position position); + boolean isCannon(Position position); + Piece getPiece(Position position); +} diff --git a/src/main/java/domain/Team.java b/src/main/java/domain/Team.java new file mode 100644 index 0000000000..d6e9a33e53 --- /dev/null +++ b/src/main/java/domain/Team.java @@ -0,0 +1,7 @@ +package domain; + +public enum Team { + CHO, + HAN, + NONE +} diff --git a/src/main/java/domain/dto/JanggiBoardDTO.java b/src/main/java/domain/dto/JanggiBoardDTO.java new file mode 100644 index 0000000000..2e93cfd2b9 --- /dev/null +++ b/src/main/java/domain/dto/JanggiBoardDTO.java @@ -0,0 +1,14 @@ +package domain.dto; + +import domain.JanggiBoard; +import java.util.List; + +public record JanggiBoardDTO(List janggiBoardDto) { + public static JanggiBoardDTO from(JanggiBoard janggiBoard) { + List pieceDtos = janggiBoard.getJanggiBoard().entrySet().stream() + .map(entry -> PieceDTO.from(entry.getKey(), entry.getValue())) + .toList(); + + return new JanggiBoardDTO(pieceDtos); + } +} diff --git a/src/main/java/domain/dto/PieceDTO.java b/src/main/java/domain/dto/PieceDTO.java new file mode 100644 index 0000000000..9f891e6d5b --- /dev/null +++ b/src/main/java/domain/dto/PieceDTO.java @@ -0,0 +1,11 @@ +package domain.dto; + +import domain.Team; +import domain.piece.Piece; +import domain.position.Position; + +public record PieceDTO(int row, int col, Team team) { + public static PieceDTO from(Position position, Piece piece) { + return new PieceDTO(position.row(), position.col(), piece.getTeam()); + } +} diff --git a/src/main/java/domain/piece/Blank.java b/src/main/java/domain/piece/Blank.java new file mode 100644 index 0000000000..f95bc9186e --- /dev/null +++ b/src/main/java/domain/piece/Blank.java @@ -0,0 +1,18 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.Team; +import domain.position.Position; +import domain.strategy.NoStrategy; + +public class Blank extends Piece { + + public Blank() { + super(Team.NONE, new NoStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + return false; + } +} diff --git a/src/main/java/domain/piece/Cannon.java b/src/main/java/domain/piece/Cannon.java new file mode 100644 index 0000000000..fd616c9960 --- /dev/null +++ b/src/main/java/domain/piece/Cannon.java @@ -0,0 +1,27 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.CannonStrategy; + +import domain.strategy.Strategy; +import java.util.List; + +public class Cannon extends Piece { + + public Cannon(Team team) { + super(team, new CannonStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Car.java b/src/main/java/domain/piece/Car.java new file mode 100644 index 0000000000..2229cd2a64 --- /dev/null +++ b/src/main/java/domain/piece/Car.java @@ -0,0 +1,28 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.CarStrategy; +import domain.strategy.Strategy; + +import java.util.List; + +public class Car extends Piece { + + public Car(Team team) { + super(team, new CarStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Elephant.java b/src/main/java/domain/piece/Elephant.java new file mode 100644 index 0000000000..195abe0367 --- /dev/null +++ b/src/main/java/domain/piece/Elephant.java @@ -0,0 +1,27 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.ElephantStrategy; +import domain.strategy.Strategy; + +import java.util.List; + +public class Elephant extends Piece { + + public Elephant(Team team) { + super(team, new ElephantStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Guard.java b/src/main/java/domain/piece/Guard.java new file mode 100644 index 0000000000..a3112b5c4b --- /dev/null +++ b/src/main/java/domain/piece/Guard.java @@ -0,0 +1,28 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.Strategy; +import domain.strategy.PalaceStrategy; + +import java.util.List; + +public class Guard extends Piece { + + public Guard(Team team) { + super(team, new PalaceStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + boolean isTargetPositionBlank = pieceProvider.isBlank(to); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to) && isTargetPositionBlank) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Horse.java b/src/main/java/domain/piece/Horse.java new file mode 100644 index 0000000000..93be3fedcf --- /dev/null +++ b/src/main/java/domain/piece/Horse.java @@ -0,0 +1,27 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.HorseStrategy; +import domain.strategy.Strategy; + +import java.util.List; + +public class Horse extends Piece { + + public Horse(Team team) { + super(team, new HorseStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/King.java b/src/main/java/domain/piece/King.java new file mode 100644 index 0000000000..43b825aeb6 --- /dev/null +++ b/src/main/java/domain/piece/King.java @@ -0,0 +1,28 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.Strategy; +import domain.strategy.PalaceStrategy; + +import java.util.List; + +public class King extends Piece { + + public King(Team team) { + super(team, new PalaceStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + boolean isTargetPositionBlank = pieceProvider.isBlank(to); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to) && isTargetPositionBlank) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Pawn.java b/src/main/java/domain/piece/Pawn.java new file mode 100644 index 0000000000..9ea7eae619 --- /dev/null +++ b/src/main/java/domain/piece/Pawn.java @@ -0,0 +1,28 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.Strategy; +import domain.strategy.PawnStrategy; + +import java.util.List; + +public class Pawn extends Piece{ + + public Pawn(Team team) { + super(team, new PawnStrategy()); + } + + @Override + public boolean canMove(Position from, Position to, PieceProvider pieceProvider) { + List moveCandidates = moveStrategy.getMoveCandidates(from, pieceProvider); + boolean isTargetPositionBlank = pieceProvider.isBlank(to); + for (Position candidatePosition : moveCandidates) { + if (candidatePosition.equals(to) && isTargetPositionBlank) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/domain/piece/Piece.java b/src/main/java/domain/piece/Piece.java new file mode 100644 index 0000000000..9f00dc2369 --- /dev/null +++ b/src/main/java/domain/piece/Piece.java @@ -0,0 +1,27 @@ +package domain.piece; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.strategy.Strategy; +import java.util.List; + +public abstract class Piece { + private final Team team; + protected final Strategy moveStrategy; + + public Piece(Team team, final Strategy moveStrategy) { + this.team = team; + this.moveStrategy = moveStrategy; + } + + public Team getTeam() { + return team; + } + + public abstract boolean canMove(Position from, Position to, PieceProvider pieceProvider); + + public List getMoveCandidates(Position from, PieceProvider board) { + return moveStrategy.getMoveCandidates(from, board); + } +} diff --git a/src/main/java/domain/position/Position.java b/src/main/java/domain/position/Position.java new file mode 100644 index 0000000000..6b6aa3c7d7 --- /dev/null +++ b/src/main/java/domain/position/Position.java @@ -0,0 +1,10 @@ +package domain.position; + +public record Position(int row, int col) { + + public Position { + if (row < 0 || col < 0) { + throw new IllegalArgumentException("좌표는 음수일 수 없습니다."); + } + } +} diff --git a/src/main/java/domain/strategy/CannonStrategy.java b/src/main/java/domain/strategy/CannonStrategy.java new file mode 100644 index 0000000000..07f16e4cca --- /dev/null +++ b/src/main/java/domain/strategy/CannonStrategy.java @@ -0,0 +1,72 @@ +package domain.strategy; + +import static domain.Index.BOARD_COLUMNS; +import static domain.Index.BOARD_ROWS; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class CannonStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; + + return Arrays.stream(straightDirections) + .flatMap(direction -> addCannonCandidates(from, direction, board).stream()) + .collect(Collectors.toList()); + } + + private List addCannonCandidates(Position currentPosition, Direction direction, PieceProvider board) { + Position bridge = findFirstPiece(currentPosition, direction, board); + boolean isCannon = board.isCannon(bridge); + + if (!isWithinBoard(bridge) || isCannon) { + return Collections.emptyList(); + } + + return collectTargets(bridge, direction, board); + } + + private Position findFirstPiece(Position position, Direction direction, PieceProvider board) { + Position nextPosition = getNext(position, direction); + while (isWithinBoard(nextPosition) && board.isBlank(nextPosition)) { + nextPosition = getNext(nextPosition, direction); + } + return nextPosition; + } + + private Position getNext(Position position, Direction direction) { + int nextRows = position.row() + direction.getRowOffset(); + int nextColumns = position.col() + direction.getColOffset(); + return new Position(nextRows, nextColumns); + } + + private List collectTargets(Position bridge, Direction direction, PieceProvider board) { + List candidates = new ArrayList<>(); + Position target = getNext(bridge, direction); + + while (isWithinBoard(target) && board.isBlank(target)) { + candidates.add(target); + target = getNext(target, direction); + } + + boolean isCannon = board.isCannon(target); + if (isWithinBoard(target) && !isCannon) { + candidates.add(target); + } + + return candidates; + } + + private boolean isWithinBoard(Position position) { + return position.row() >= 0 && position.row() < BOARD_ROWS.getIndex() && + position.col() >= 0 && position.col() < BOARD_COLUMNS.getIndex(); + } +} diff --git a/src/main/java/domain/strategy/CarStrategy.java b/src/main/java/domain/strategy/CarStrategy.java new file mode 100644 index 0000000000..78047ca6f6 --- /dev/null +++ b/src/main/java/domain/strategy/CarStrategy.java @@ -0,0 +1,50 @@ +package domain.strategy; + +import static domain.Index.BOARD_COLUMNS; +import static domain.Index.BOARD_ROWS; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class CarStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + + Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; + + return Arrays.stream(Direction.values()) // 모든 방향 혹은 특정 방향 배열 + .flatMap(direction -> addPathCandidates(from, direction, board).stream()) + .collect(Collectors.toList()); + } + + private List addPathCandidates(Position currentPosition, Direction direction, PieceProvider board) { + List candidates = new ArrayList<>(); + Position next = currentPosition; + + while (true) { + int nextRows = next.row() + direction.getRowOffset(); + int nextColumns = next.col() + direction.getColOffset(); + + if (nextRows < 0 || nextRows >= BOARD_ROWS.getIndex() || nextColumns < 0 + || nextColumns >= BOARD_COLUMNS.getIndex()) { + break; + } + + next = new Position(nextRows, nextColumns); + if (board.isBlank(next)) { + candidates.add(next); + continue; + } + candidates.add(next); + break; + } + + return candidates; + } +} diff --git a/src/main/java/domain/strategy/Direction.java b/src/main/java/domain/strategy/Direction.java new file mode 100644 index 0000000000..d69cef20e1 --- /dev/null +++ b/src/main/java/domain/strategy/Direction.java @@ -0,0 +1,28 @@ +package domain.strategy; + +public enum Direction { + NORTH(-1, 0), + SOUTH(1, 0), + WEST(0, -1), + EAST(0, 1), + NORTH_WEST(-1, -1), + NORTH_EAST(-1, 1), + SOUTH_WEST(1, -1), + SOUTH_EAST(1, 1); + + private final int rowOffset; + private final int colOffset; + + Direction(int rowOffset, int colOffset) { + this.rowOffset = rowOffset; + this.colOffset = colOffset; + } + + public int getRowOffset() { + return rowOffset; + } + + public int getColOffset() { + return colOffset; + } +} diff --git a/src/main/java/domain/strategy/ElephantStrategy.java b/src/main/java/domain/strategy/ElephantStrategy.java new file mode 100644 index 0000000000..6189df189d --- /dev/null +++ b/src/main/java/domain/strategy/ElephantStrategy.java @@ -0,0 +1,48 @@ +package domain.strategy; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.List; + +public class ElephantStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + List candidates = new ArrayList<>(); + Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; + + for (Direction straight : straightDirections) { + Position myeok1 = new Position( + from.row() + straight.getRowOffset(), + from.col() + straight.getColOffset() + ); + + if (!board.isBlank(myeok1)) continue; + + for (Direction diag : getDiagonalsFor(straight)) { + Position myeok2 = new Position( + myeok1.row() + diag.getRowOffset(), + myeok1.col() + diag.getColOffset() + ); + + if (!board.isBlank(myeok2)) continue; + + Position target = new Position(myeok2.row() + diag.getRowOffset(), + myeok2.col() + diag.getColOffset() + ); + candidates.add(target); + } + } + return candidates; + } + + private List getDiagonalsFor(Direction straight) { + if (straight == Direction.NORTH) return List.of(Direction.NORTH_WEST, Direction.NORTH_EAST); + if (straight == Direction.SOUTH) return List.of(Direction.SOUTH_WEST, Direction.SOUTH_EAST); + if (straight == Direction.WEST) return List.of(Direction.NORTH_WEST, Direction.SOUTH_WEST); + if (straight == Direction.EAST) return List.of(Direction.NORTH_EAST, Direction.SOUTH_EAST); + return List.of(); + } +} diff --git a/src/main/java/domain/strategy/HorseStrategy.java b/src/main/java/domain/strategy/HorseStrategy.java new file mode 100644 index 0000000000..b6bdd56841 --- /dev/null +++ b/src/main/java/domain/strategy/HorseStrategy.java @@ -0,0 +1,43 @@ +package domain.strategy; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.List; + +public class HorseStrategy implements Strategy { + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + List candidates = new ArrayList<>(); + + Direction[] straightDirections = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; + + for (Direction straight : straightDirections) { + // 1칸 직선 방향으로 가기 + int myeokRow = from.row() + straight.getRowOffset(); + int myeokCol = from.col() + straight.getColOffset(); + Position myeokPosition = new Position(myeokRow, myeokCol); + + if (board.isBlank(myeokPosition)) { + List diagonals = getDiagonalsFor(straight); + for (Direction diag : diagonals) { + int targetRow = myeokPosition.row() + diag.getRowOffset(); + int targetCol = myeokPosition.col() + diag.getColOffset(); + Position targetPosition = new Position(targetRow, targetCol); + + candidates.add(targetPosition); + } + } + } + return candidates; + } + + private List getDiagonalsFor(Direction straight) { + if (straight == Direction.NORTH) return List.of(Direction.NORTH_WEST, Direction.NORTH_EAST); + if (straight == Direction.SOUTH) return List.of(Direction.SOUTH_WEST, Direction.SOUTH_EAST); + if (straight == Direction.WEST) return List.of(Direction.NORTH_WEST, Direction.SOUTH_WEST); + if (straight == Direction.EAST) return List.of(Direction.NORTH_EAST, Direction.SOUTH_EAST); + return List.of(); + } +} diff --git a/src/main/java/domain/strategy/NoStrategy.java b/src/main/java/domain/strategy/NoStrategy.java new file mode 100644 index 0000000000..8b2a334b3f --- /dev/null +++ b/src/main/java/domain/strategy/NoStrategy.java @@ -0,0 +1,13 @@ +package domain.strategy; + +import domain.PieceProvider; +import domain.position.Position; +import java.util.List; + +public class NoStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + throw new IllegalArgumentException("빈 칸은 이동할 수 없습니다."); + } +} diff --git a/src/main/java/domain/strategy/PalaceStrategy.java b/src/main/java/domain/strategy/PalaceStrategy.java new file mode 100644 index 0000000000..9be702b749 --- /dev/null +++ b/src/main/java/domain/strategy/PalaceStrategy.java @@ -0,0 +1,27 @@ +package domain.strategy; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.List; + +public class PalaceStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + List candidates = new ArrayList<>(); + + Direction[] directions = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST, + Direction.NORTH_EAST, Direction.NORTH_WEST, Direction.SOUTH_EAST, Direction.SOUTH_WEST}; + + for (Direction direction : directions) { + int targetRow = from.row() + direction.getRowOffset(); + int targetColumns = from.col() + direction.getColOffset(); + + Position targetPosition = new Position(targetRow, targetColumns); + candidates.add(targetPosition); + } + return candidates; + } +} diff --git a/src/main/java/domain/strategy/PawnStrategy.java b/src/main/java/domain/strategy/PawnStrategy.java new file mode 100644 index 0000000000..3733c1e4b4 --- /dev/null +++ b/src/main/java/domain/strategy/PawnStrategy.java @@ -0,0 +1,25 @@ +package domain.strategy; + +import domain.position.Position; +import domain.PieceProvider; + +import java.util.ArrayList; +import java.util.List; + +public class PawnStrategy implements Strategy { + + @Override + public List getMoveCandidates(Position from, PieceProvider board) { + List candidates = new ArrayList<>(); + Direction[] directions = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; + + for (Direction direction : directions) { + int targetRow = from.row() + direction.getRowOffset(); + int targetColumns = from.col() + direction.getColOffset(); + + Position targetPosition = new Position(targetRow, targetColumns); + candidates.add(targetPosition); + } + return candidates; + } +} diff --git a/src/main/java/domain/strategy/Strategy.java b/src/main/java/domain/strategy/Strategy.java new file mode 100644 index 0000000000..090261e0e5 --- /dev/null +++ b/src/main/java/domain/strategy/Strategy.java @@ -0,0 +1,12 @@ +package domain.strategy; + +import domain.piece.Blank; +import domain.piece.Piece; +import domain.position.Position; +import domain.PieceProvider; + +import java.util.List; + +public interface Strategy { + List getMoveCandidates(Position from, PieceProvider board); +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 0000000000..097c2037c1 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,17 @@ +package view; + +import java.util.Scanner; + +public class InputView { + private final Scanner sc = new Scanner(System.in); + + public String inputMovePiece() { + System.out.println("어떤 기물을 옮기시겠습니까?(예시 : 0,0)"); + return sc.nextLine(); + } + + public String inputTargetPosition() { + System.out.println("어디로 옮기시겠습니까?(예시 : 1,0)"); + return sc.nextLine(); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 0000000000..e3e487554b --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,72 @@ +package view; + +import domain.JanggiBoard; +import domain.piece.Cannon; +import domain.piece.Car; +import domain.piece.Elephant; +import domain.piece.Guard; +import domain.piece.Horse; +import domain.piece.King; +import domain.piece.Pawn; +import domain.position.Position; +import domain.piece.Piece; + +public class OutputView { + private static final int BOARD_ROWS = 10; + private static final int BOARD_COLUMNS = 9; + + public void printBoard(JanggiBoard board) { + printColumnIndices(); + for (int row = 0; row < BOARD_ROWS; row++) { + System.out.printf("%2d ", row); + printRow(board, row); + System.out.println(); + } + } + + private void printColumnIndices() { + System.out.print(" "); + for (int col = 0; col < BOARD_COLUMNS; col++) { + System.out.print(col + "."); + } + System.out.println(); + } + + private void printRow(JanggiBoard board, int row) { + for (int col = 0; col < BOARD_COLUMNS; col++) { + Position position = new Position(row, col); + Piece piece = board.getPiece(position); + System.out.print(getSymbol(piece) + " "); + } + } + + public void printErrorMessage(IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + + + private String getSymbol(Piece piece) { + if (piece instanceof Cannon) { + return "포"; + } + if (piece instanceof Car) { + return "차"; + } + if (piece instanceof Elephant) { + return "상"; + } + if (piece instanceof Guard) { + return "사"; + } + if (piece instanceof Horse) { + return "마"; + } + if (piece instanceof King) { + return "궁"; + } + if (piece instanceof Pawn) { + return "졸"; + } + return "."; + } +} diff --git a/src/test/java/boardSetting/BoardInitialTest.java b/src/test/java/boardSetting/BoardInitialTest.java new file mode 100644 index 0000000000..135163247d --- /dev/null +++ b/src/test/java/boardSetting/BoardInitialTest.java @@ -0,0 +1,172 @@ +package boardSetting; + +import domain.JanggiBoard; +import domain.JanggiBoardInitializer; +import domain.position.Position; +import domain.Team; +import domain.piece.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class BoardInitialTest { + + private JanggiBoard janggiBoard; + + @BeforeEach + void setUp() { + janggiBoard = new JanggiBoard(new JanggiBoardInitializer()); + } + + @Test + void 보드_초기화시_90칸이_제대로_생성되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.size()).isEqualTo(90); + } + + @Test + void 한나라_차가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(0, 0))).isInstanceOf(Car.class); + assertThat(board.get(new Position(0, 8))).isInstanceOf(Car.class); + + assertThat(board.get(new Position(0, 0)).getTeam()).isEqualTo(Team.HAN); + assertThat(board.get(new Position(0, 8)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_차가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(9, 0))).isInstanceOf(Car.class); + assertThat(board.get(new Position(9, 8))).isInstanceOf(Car.class); + + assertThat(board.get(new Position(9, 0)).getTeam()).isEqualTo(Team.CHO); + assertThat(board.get(new Position(9, 8)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_마가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(0, 1))).isInstanceOf(Horse.class); + assertThat(board.get(new Position(0, 6))).isInstanceOf(Horse.class); + + assertThat(board.get(new Position(0, 1)).getTeam()).isEqualTo(Team.HAN); + assertThat(board.get(new Position(0, 6)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_마가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(9, 1))).isInstanceOf(Horse.class); + assertThat(board.get(new Position(9, 6))).isInstanceOf(Horse.class); + + assertThat(board.get(new Position(9, 1)).getTeam()).isEqualTo(Team.CHO); + assertThat(board.get(new Position(9, 6)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_상이_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(0, 2))).isInstanceOf(Elephant.class); + assertThat(board.get(new Position(0, 7))).isInstanceOf(Elephant.class); + + assertThat(board.get(new Position(0, 2)).getTeam()).isEqualTo(Team.HAN); + assertThat(board.get(new Position(0, 7)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_상이_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(9, 2))).isInstanceOf(Elephant.class); + assertThat(board.get(new Position(9, 7))).isInstanceOf(Elephant.class); + + assertThat(board.get(new Position(9, 2)).getTeam()).isEqualTo(Team.CHO); + assertThat(board.get(new Position(9, 7)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_사가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(0, 3))).isInstanceOf(Guard.class); + assertThat(board.get(new Position(0, 5))).isInstanceOf(Guard.class); + + assertThat(board.get(new Position(0, 3)).getTeam()).isEqualTo(Team.HAN); + assertThat(board.get(new Position(0, 5)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_사가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(9, 3))).isInstanceOf(Guard.class); + assertThat(board.get(new Position(9, 5))).isInstanceOf(Guard.class); + + assertThat(board.get(new Position(9, 3)).getTeam()).isEqualTo(Team.CHO); + assertThat(board.get(new Position(9, 5)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_궁이_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(1, 4))).isInstanceOf(King.class); + assertThat(board.get(new Position(1, 4)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_궁이_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(8, 4))).isInstanceOf(King.class); + assertThat(board.get(new Position(8, 4)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_포가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(2, 1))).isInstanceOf(Cannon.class); + assertThat(board.get(new Position(2, 7))).isInstanceOf(Cannon.class); + + assertThat(board.get(new Position(2, 1)).getTeam()).isEqualTo(Team.HAN); + assertThat(board.get(new Position(2, 7)).getTeam()).isEqualTo(Team.HAN); + } + + @Test + void 초나라_포가_제대로된_위치에_초기화_되었는지_확인한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(board.get(new Position(7, 1))).isInstanceOf(Cannon.class); + assertThat(board.get(new Position(7, 7))).isInstanceOf(Cannon.class); + + assertThat(board.get(new Position(7, 1)).getTeam()).isEqualTo(Team.CHO); + assertThat(board.get(new Position(7, 7)).getTeam()).isEqualTo(Team.CHO); + } + + @Test + void 한나라_졸_위치들을_한번에_검증한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(List.of(new Position(3, 0), + new Position(3, 2), + new Position(3, 4), + new Position(3,6), + new Position(3,8))) + .allSatisfy(pos -> { + assertThat(board.get(pos)).isInstanceOf(Pawn.class); + assertThat(board.get(pos).getTeam()).isEqualTo(Team.HAN); + }); + } + + @Test + void 초나라_졸_위치들을_한번에_검증한다() { + Map board = janggiBoard.getJanggiBoard(); + assertThat(List.of(new Position(6, 0), + new Position(6, 2), + new Position(6, 4), + new Position(6,6), + new Position(6,8))) + .allSatisfy(pos -> { + assertThat(board.get(pos)).isInstanceOf(Pawn.class); + assertThat(board.get(pos).getTeam()).isEqualTo(Team.CHO); + }); + } +} diff --git a/src/test/java/boardSetting/PieceTest/CannonTest.java b/src/test/java/boardSetting/PieceTest/CannonTest.java new file mode 100644 index 0000000000..5bcecdb85f --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/CannonTest.java @@ -0,0 +1,89 @@ +package boardSetting.PieceTest; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.piece.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CannonTest { + + private Cannon cannon; + private TestPieceProvider testBoard; + + @BeforeEach + public void setUp() { + cannon = new Cannon(Team.CHO); + testBoard = new TestPieceProvider(); + } + + @Test + void 포는_다리를_넘어_이동할_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(8, 5); + + testBoard.setAllBlank(); + testBoard.setPiece(new Position(6, 5), new Guard(Team.CHO)); + + boolean isCannonMove = cannon.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCannonMove).isTrue(); + } + + @Test + void 포는_포를_다리로_삼을_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(8, 5); + + testBoard.setAllBlank(); + testBoard.setPiece(new Position(6, 5), new Cannon(Team.CHO)); + + boolean isCannonMove = cannon.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCannonMove).isFalse(); + } + + @Test + void 포는_포를_잡을_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(8, 5); + + testBoard.setAllBlank(); + testBoard.setPiece(new Position(6, 5), new Guard(Team.CHO)); + testBoard.setPiece(new Position(8, 5), new Cannon(Team.HAN)); + + boolean isCannonMove = cannon.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCannonMove).isFalse(); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map pieces = new HashMap<>(); + + void setPiece(Position pos, Piece piece) { + pieces.put(pos, piece); + } + + void setAllBlank() { + pieces.clear(); + } + + @Override + public boolean isBlank(Position position) { + return !pieces.containsKey(position); + } + + @Override + public boolean isCannon(Position position) { + return true; + } + + @Override + public Piece getPiece(Position position) { + return pieces.getOrDefault(position, new Blank()); + } + } +} diff --git a/src/test/java/boardSetting/PieceTest/CarTest.java b/src/test/java/boardSetting/PieceTest/CarTest.java new file mode 100644 index 0000000000..5aff7173c2 --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/CarTest.java @@ -0,0 +1,79 @@ +package boardSetting.PieceTest; + +import domain.position.Position; +import domain.Team; +import domain.piece.Blank; +import domain.piece.Car; +import domain.piece.Piece; +import domain.PieceProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class CarTest { + + private Car car; + private TestPieceProvider testBoard; + + @BeforeEach + public void setUp() { + car = new Car(Team.CHO); + testBoard = new TestPieceProvider(); + } + + @Test + void 차는_목적지에_갈_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(7, 5); + + testBoard.setAllBlank(); + boolean isCarMove = car.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCarMove).isTrue(); + } + + @Test + void 차는_목적지에_갈_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(7, 5); + + testBoard.setAllBlank(); + testBoard.setBlank(new Position(6, 5)); + + boolean isCarMove = car.canMove(currentPosition, targetPosition, testBoard); + + assertThat(isCarMove).isFalse(); + } + + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/PieceTest/ElephantTest.java b/src/test/java/boardSetting/PieceTest/ElephantTest.java new file mode 100644 index 0000000000..3a499895ef --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/ElephantTest.java @@ -0,0 +1,75 @@ +package boardSetting.PieceTest; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.piece.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ElephantTest { + + private TestPieceProvider testBoard; + private Elephant elephant; + + @BeforeEach + void setUp() { + elephant = new Elephant(Team.CHO); + testBoard = new TestPieceProvider(); + } + + @Test + void 마가_목적지에_갈_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(3, 8); + + testBoard.setAllBlank(); + boolean isCanMove = elephant.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCanMove).isTrue(); + } + + @Test + void 마가_목적지에_갈_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(3, 8); + + testBoard.setAllBlank(); + testBoard.setBlank(new Position(5, 6)); + + boolean isCanMove = elephant.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCanMove).isFalse(); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/PieceTest/HorseTest.java b/src/test/java/boardSetting/PieceTest/HorseTest.java new file mode 100644 index 0000000000..d551e1b4e2 --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/HorseTest.java @@ -0,0 +1,76 @@ +package boardSetting.PieceTest; + +import domain.position.Position; +import domain.Team; +import domain.piece.Blank; +import domain.piece.Horse; +import domain.piece.Piece; +import domain.PieceProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class HorseTest { + private TestPieceProvider testBoard; + private Horse horse; + + @BeforeEach + void setUp() { + horse = new Horse(Team.CHO); + testBoard = new TestPieceProvider(); + } + + @Test + void 마가_목적지에_갈_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(3, 4); + + testBoard.setAllBlank(); + boolean isCanMove = horse.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCanMove).isTrue(); + } + + @Test + void 마가_목적지에_갈_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(3, 4); + + testBoard.setAllBlank(); + testBoard.setBlank(new Position(4, 5)); + + boolean isCanMove = horse.canMove(currentPosition, targetPosition, testBoard); + assertThat(isCanMove).isFalse(); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/PieceTest/PalaceTest.java b/src/test/java/boardSetting/PieceTest/PalaceTest.java new file mode 100644 index 0000000000..e73995b10f --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/PalaceTest.java @@ -0,0 +1,99 @@ +package boardSetting.PieceTest; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.piece.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class PalaceTest { + + private TestPieceProvider testBoard; + private King king; + private Guard guard; + + @BeforeEach + void setUp() { + king = new King(Team.CHO); + guard = new Guard(Team.CHO); + testBoard = new TestPieceProvider(); + } + + @Test + void 궁이_목적지에_갈_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(4, 5); + + testBoard.setAllBlank(); + boolean isKingMove = king.canMove(currentPosition, targetPosition, testBoard); + assertThat(isKingMove).isTrue(); + + } + + @Test + void 궁이_목적지에_갈_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(4, 5); + + testBoard.setAllBlank(); + testBoard.setBlank(new Position(4, 5)); + boolean isKingMove = king.canMove(currentPosition, targetPosition, testBoard); + assertThat(isKingMove).isFalse(); + } + + @Test + void 사가_목적지에_갈_수_있다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(4, 5); + + testBoard.setAllBlank(); + boolean isGuardMove = guard.canMove(currentPosition, targetPosition, testBoard); + assertThat(isGuardMove).isTrue(); + } + + @Test + void 사가_목적지에_갈_수_없다() { + Position currentPosition = new Position(5, 5); + Position targetPosition = new Position(4, 5); + + testBoard.setAllBlank(); + testBoard.setBlank(new Position(4, 5)); + boolean isGuardMove = guard.canMove(currentPosition, targetPosition, testBoard); + assertThat(isGuardMove).isFalse(); + } + + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/PieceTest/PawnTest.java b/src/test/java/boardSetting/PieceTest/PawnTest.java new file mode 100644 index 0000000000..112c1f77ad --- /dev/null +++ b/src/test/java/boardSetting/PieceTest/PawnTest.java @@ -0,0 +1,4 @@ +package boardSetting.PieceTest; + +public class PawnTest { +} diff --git a/src/test/java/boardSetting/strategyTest/CannonStrategyTest.java b/src/test/java/boardSetting/strategyTest/CannonStrategyTest.java new file mode 100644 index 0000000000..3ff4b5b7d5 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/CannonStrategyTest.java @@ -0,0 +1,93 @@ +package boardSetting.strategyTest; + +import domain.PieceProvider; +import domain.position.Position; +import domain.Team; +import domain.piece.*; +import domain.strategy.CannonStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class CannonStrategyTest { + + private CannonStrategy cannonStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + public void setUp() { + cannonStrategy = new CannonStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 포가_기물_한개를_넘어_빈칸으로_이동하는지_확인한다() { + Position currentPosition = new Position(0, 0); + testBoard.setPiece(new Position(2, 0), new Guard(Team.CHO)); + + List candidates = cannonStrategy.getMoveCandidates(currentPosition, testBoard); + assertThat(candidates).contains(new Position(3, 0), new Position(9, 0)); + assertThat(candidates).doesNotContain(new Position(1, 0), new Position(2, 0)); + } + + @Test + void 포는_다른포를_건너뛸_수_없다() { + Position currentPosition = new Position(0, 0); + testBoard.setPiece(new Position(2, 0), new Cannon(Team.CHO)); + + List candidates = cannonStrategy.getMoveCandidates(currentPosition, testBoard); + assertThat(candidates.size()).isEqualTo(0); + } + + @Test + void 포는_기물이_없으면_이동할_수_없다() { + Position currentPosition = new Position(0, 0); + testBoard.setAllBlank(); + + List candidates = cannonStrategy.getMoveCandidates(currentPosition, testBoard); + assertThat(candidates.size()).isEqualTo(0); + } + + @Test + void 포는_다른포를_잡을_수_없다() { + Position currentPosition = new Position(0, 0); + testBoard.setPiece(new Position(2, 0), new Guard(Team.CHO)); + testBoard.setPiece(new Position(4, 0), new Cannon(Team.HAN)); + + List candidates = cannonStrategy.getMoveCandidates(currentPosition, testBoard); + assertThat(candidates).contains(new Position(3, 0)); + assertThat(candidates).doesNotContain(new Position(4, 0)); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map pieces = new HashMap<>(); + + void setPiece(Position position, Piece piece) { + pieces.put(position, piece); + } + + void setAllBlank() { + pieces.clear(); + } + + @Override + public boolean isBlank(Position position) { + return !pieces.containsKey(position); + } + + @Override + public boolean isCannon(Position position) { + return true; + } + + @Override + public Piece getPiece(Position position) { + return pieces.getOrDefault(position, new Blank()); + } + } +} diff --git a/src/test/java/boardSetting/strategyTest/CarStrategyTest.java b/src/test/java/boardSetting/strategyTest/CarStrategyTest.java new file mode 100644 index 0000000000..ca14db0863 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/CarStrategyTest.java @@ -0,0 +1,85 @@ +package boardSetting.strategyTest; + +import domain.position.Position; +import domain.piece.Blank; +import domain.piece.Piece; +import domain.PieceProvider; +import domain.strategy.CarStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CarStrategyTest { + + private CarStrategy carStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + void setUp() { + carStrategy = new CarStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 차는_상하좌우_직선_모든칸_후보로_반환한다() { + Position position = new Position(5, 4); + testBoard.setAllBlank(); + + List candidates = carStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(17); + assertThat(candidates).contains(new Position(0, 4), + new Position(9, 4), + new Position(5, 0), + new Position(5, 8)); + } + + @Test + void 이동경로에_장애물_있으면_그_지점까지_이동후_전진하지_않는다() { + Position position = new Position(5, 4); + testBoard.setAllBlank(); + + Position obstacle = new Position(3, 4); + testBoard.setBlank(obstacle); + + List candidates = carStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).contains(new Position(4, 4), new Position(3, 4)); + assertThat(candidates).doesNotContain(new Position(2, 4), + new Position(1, 4), + new Position(0, 4)); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position position) { + boardState.put(position, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/strategyTest/ElephantStrategyTest.java b/src/test/java/boardSetting/strategyTest/ElephantStrategyTest.java new file mode 100644 index 0000000000..0760eae367 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/ElephantStrategyTest.java @@ -0,0 +1,120 @@ +package boardSetting.strategyTest; + +import domain.position.Position; +import domain.piece.Blank; +import domain.piece.Piece; +import domain.PieceProvider; +import domain.strategy.ElephantStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ElephantStrategyTest { + + private ElephantStrategy elephantStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + public void setUp() { + elephantStrategy = new ElephantStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 상_주변에_장애물_없으면_8가지_후보_모두_반환() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + List candidates = elephantStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(8) + .containsExactlyInAnyOrder( + new Position(2, 3), new Position(2, 7), // 북쪽 기반 + new Position(3, 8), new Position(7, 8), // 남쪽 기반 + new Position(8, 3), new Position(8, 7), // 서쪽 기반 + new Position(3, 2), new Position(7, 2) // 동쪽 기반 + ); + } + + @Test + void 북쪽_멱이_막혀있으면_북서_북동으로_이동할수_없다(){ + Position source = new Position(5, 5); + testBoard.setAllBlank(); + + // 북쪽 멱 위치를 막힌 상태로 설정 + testBoard.setBlank(new Position(4, 5)); + List candidates = elephantStrategy.getMoveCandidates(source, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(2, 3), new Position(2, 7)); + } + + @Test + void 남쪽_멱이_막혀있으면_남서_남동으로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(6, 5)); + List candidates = elephantStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(8,3), new Position(8,7)); + } + + @Test + void 서쪽_멱이_막혀있으면_북서_남서로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(5, 4)); + List candidates = elephantStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(3,2), new Position(7,2)); + } + + @Test + void 동쪽_멱이_막혀있으면_북동_남동으로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(5, 6)); + List candidates = elephantStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(3, 8), new Position(7, 8)); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/strategyTest/HorseStrategyTest.java b/src/test/java/boardSetting/strategyTest/HorseStrategyTest.java new file mode 100644 index 0000000000..5656857d02 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/HorseStrategyTest.java @@ -0,0 +1,120 @@ +package boardSetting.strategyTest; + + +import domain.position.Position; +import domain.piece.Blank; +import domain.piece.Piece; +import domain.PieceProvider; +import domain.strategy.HorseStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class HorseStrategyTest { + private HorseStrategy horseStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + void setUp() { + horseStrategy = new HorseStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 마_주변에_장애물_없으면_8가지_후보_모두_반환() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + List candidates = horseStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(8) + .containsExactlyInAnyOrder( + new Position(3, 4), new Position(3, 6), // 북쪽 기반 + new Position(7, 4), new Position(7, 6), // 남쪽 기반 + new Position(4, 3), new Position(6, 3), // 서쪽 기반 + new Position(4, 7), new Position(6, 7) // 동쪽 기반 + ); + } + + @Test + void 북쪽_멱이_막혀있으면_북서_북동으로_이동할수_없다(){ + Position source = new Position(5, 5); + testBoard.setAllBlank(); + + // 북쪽 멱 위치를 막힌 상태로 설정 + testBoard.setBlank(new Position(4, 5)); + List candidates = horseStrategy.getMoveCandidates(source, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(3, 4), new Position(3, 6)); + } + + @Test + void 남쪽_멱이_막혀있으면_남서_남동으로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(6, 5)); + List candidates = horseStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(7,4), new Position(7,6)); + } + + @Test + void 서쪽_멱이_막혀있으면_북서_남서로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(5, 4)); + List candidates = horseStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(6,5), new Position(4,3)); + } + + @Test + void 동쪽_멱이_막혀있으면_북동_남동으로_이동할수_없다() { + Position position = new Position(5, 5); + testBoard.setAllBlank(); + + testBoard.setBlank(new Position(5, 6)); + List candidates = horseStrategy.getMoveCandidates(position, testBoard); + + assertThat(candidates).hasSize(6) + .doesNotContain(new Position(4, 7), new Position(6, 7)); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/strategyTest/PalaceStrategyTest.java b/src/test/java/boardSetting/strategyTest/PalaceStrategyTest.java new file mode 100644 index 0000000000..d399255f99 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/PalaceStrategyTest.java @@ -0,0 +1,71 @@ +package boardSetting.strategyTest; + +import domain.position.Position; +import domain.piece.Blank; +import domain.piece.Piece; +import domain.PieceProvider; +import domain.strategy.PalaceStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +public class PalaceStrategyTest { + + private PalaceStrategy palaceStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + void setUp() { + palaceStrategy = new PalaceStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 궁과_사가_주변에_장애물이_없다면_8가지_후보_모두_반환() { + Position currentPosition = new Position(5, 5); + testBoard.setAllBlank(); + + List candidates = palaceStrategy.getMoveCandidates(currentPosition, testBoard); + + assertThat(candidates).hasSize(8) + .containsExactlyInAnyOrder( + new Position(4, 5), new Position(4, 4), + new Position(4, 6), new Position(5, 4), + new Position(5, 6), new Position(6, 4), + new Position(6, 5), new Position(6, 6) + ); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +} diff --git a/src/test/java/boardSetting/strategyTest/PawnStrategyTest.java b/src/test/java/boardSetting/strategyTest/PawnStrategyTest.java new file mode 100644 index 0000000000..d7865a9c32 --- /dev/null +++ b/src/test/java/boardSetting/strategyTest/PawnStrategyTest.java @@ -0,0 +1,69 @@ +package boardSetting.strategyTest; + +import domain.position.Position; +import domain.piece.Blank; +import domain.piece.Piece; +import domain.PieceProvider; +import domain.strategy.PawnStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PawnStrategyTest { + + private PawnStrategy pawnStrategy; + private TestPieceProvider testBoard; + + @BeforeEach + void setUp() { + pawnStrategy = new PawnStrategy(); + testBoard = new TestPieceProvider(); + } + + @Test + void 졸은_4가지_이동_후보_모두_반환() { + Position currentPosition = new Position(5, 5); + testBoard.setAllBlank(); + + List candidates = pawnStrategy.getMoveCandidates(currentPosition, testBoard); + + assertThat(candidates).hasSize(4) + .containsExactlyInAnyOrder( + new Position(4, 5), new Position(6, 5), + new Position(5, 4), new Position(5, 6) + ); + } + + private static class TestPieceProvider implements PieceProvider { + private final Map boardState = new HashMap<>(); + private boolean defaultState = true; + + void setBlank(Position pos) { + boardState.put(pos, false); + } + + void setAllBlank() { + this.defaultState = true; + } + + @Override + public boolean isBlank(Position position) { + return boardState.getOrDefault(position, defaultState); + } + + @Override + public boolean isCannon(Position position) { + return false; + } + + @Override + public Piece getPiece(Position position) { + return new Blank(); + } + } +}