diff --git a/README.md b/README.md index a8ae9b8..009af49 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # java-chess 체스 게임 저장소 + +### 구현해야 할 기능 구현 목록 +[입력] +- 사용자로부터 커맨드 입력 받기 + - 시작 : start + - 종료 : end + - 이동 : move sourcePosition targetPosition + +- 게임이 끝나면 바로 종료할 지 점수 볼 지 입력 + - 종료 : end + - 점수 및 승자 보기 : status + +[게임 과정] +- a2와 같은 형태의 위치값을 좌표평면 위치로 변환하는 기능 +- 체스판 초기화 +- 각 말들의 이동 로직 구현 +- 게임 종료 기능 + - King이 잡히면 종료 +- 점수 계산 기능 + - queen : 9점 + - rook : 5점 + - bishop : 3점 + - knight : 2.5점 + - pawn + - 세로 줄에 같은 색 폰이 있으면 0.5 + - 없으면 1 + +[출력] +- 체스판 출력 기능 +- 점수 및 승자 출력 기능 diff --git a/src/main/java/chess/controller/Application.java b/src/main/java/chess/controller/Application.java new file mode 100644 index 0000000..797a125 --- /dev/null +++ b/src/main/java/chess/controller/Application.java @@ -0,0 +1,7 @@ +package chess.controller; + +public class Application { + public static void main(String[] args) { + ChessGameController.play(); + } +} diff --git a/src/main/java/chess/controller/ChessGameController.java b/src/main/java/chess/controller/ChessGameController.java new file mode 100644 index 0000000..5ca2d68 --- /dev/null +++ b/src/main/java/chess/controller/ChessGameController.java @@ -0,0 +1,24 @@ +package chess.controller; + +import chess.domain.command.Command; +import chess.domain.command.CommandMapper; +import chess.view.InputView; +import chess.view.OutputView; + +public class ChessGameController { + public static void play() { + OutputView.printGreetingMessage(); + getCommand(); + } + + private static Command getCommand() { + Command command = null; + try { + command = CommandMapper.find(InputView.getCommand()); + } catch (Exception e) { + OutputView.printMessageAndNewLine(e.getMessage()); + getCommand(); + } + return command; + } +} diff --git a/src/main/java/chess/domain/ChessBoard.java b/src/main/java/chess/domain/ChessBoard.java new file mode 100644 index 0000000..450da34 --- /dev/null +++ b/src/main/java/chess/domain/ChessBoard.java @@ -0,0 +1,14 @@ +package chess.domain; + +import chess.domain.player.Player; +import chess.domain.player.Team; + +public class ChessBoard { + private Player white; + private Player black; + + public ChessBoard() { + white = new Player(Team.WHITE); + black = new Player(Team.WHITE); + } +} diff --git a/src/main/java/chess/domain/command/Command.java b/src/main/java/chess/domain/command/Command.java new file mode 100644 index 0000000..e238649 --- /dev/null +++ b/src/main/java/chess/domain/command/Command.java @@ -0,0 +1,5 @@ +package chess.domain.command; + +public interface Command { + void execute(); +} diff --git a/src/main/java/chess/domain/command/CommandMapper.java b/src/main/java/chess/domain/command/CommandMapper.java new file mode 100644 index 0000000..c67177d --- /dev/null +++ b/src/main/java/chess/domain/command/CommandMapper.java @@ -0,0 +1,28 @@ +package chess.domain.command; + +import chess.validators.CommandValidator; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public class CommandMapper { + private static final Map, Supplier> mapper = new HashMap<>(); + + static { + mapper.put(StartCommand::isStart, StartCommand::new); + mapper.put(EndCommand::isEnd, EndCommand::new); + mapper.put(StatusCommand::isStatus, StatusCommand::new); + } + + public static Command find(String rawCommand) { + CommandValidator.validateCommand(rawCommand); + for (Map.Entry, Supplier> entry : mapper.entrySet()) { + if (entry.getKey().test(rawCommand)) { + return entry.getValue().get(); + } + } + return new MoveCommand(rawCommand); + } +} diff --git a/src/main/java/chess/domain/command/EndCommand.java b/src/main/java/chess/domain/command/EndCommand.java new file mode 100644 index 0000000..1cc0759 --- /dev/null +++ b/src/main/java/chess/domain/command/EndCommand.java @@ -0,0 +1,13 @@ +package chess.domain.command; + +public class EndCommand implements Command { + private static final String COMMAND = "end"; + + public static boolean isEnd(String operation) { + return COMMAND.equals(operation.toLowerCase()); + } + + @Override + public void execute() { + } +} diff --git a/src/main/java/chess/domain/command/MoveCommand.java b/src/main/java/chess/domain/command/MoveCommand.java new file mode 100644 index 0000000..5da85fb --- /dev/null +++ b/src/main/java/chess/domain/command/MoveCommand.java @@ -0,0 +1,22 @@ +package chess.domain.command; + +import utils.CommandSplitter; + +public class MoveCommand implements Command { + private static final String COMMAND = "move"; + private static final int SOURCE_INDEX = 0; + private static final int DESTINATION_INDEX = 1; + + private String source; + private String dest; + + public MoveCommand(String rawCommand) { + String[] splitCommand = CommandSplitter.getSourceAndDest(rawCommand); + source = splitCommand[SOURCE_INDEX]; + dest = splitCommand[DESTINATION_INDEX]; + } + + @Override + public void execute() { + } +} diff --git a/src/main/java/chess/domain/command/StartCommand.java b/src/main/java/chess/domain/command/StartCommand.java new file mode 100644 index 0000000..e30e47c --- /dev/null +++ b/src/main/java/chess/domain/command/StartCommand.java @@ -0,0 +1,16 @@ +package chess.domain.command; + +public class StartCommand implements Command { + private static final String COMMAND = "start"; + + public StartCommand() { + } + + public static boolean isStart(String operation) { + return COMMAND.equals(operation.toLowerCase()); + } + + @Override + public void execute() { + } +} diff --git a/src/main/java/chess/domain/command/StatusCommand.java b/src/main/java/chess/domain/command/StatusCommand.java new file mode 100644 index 0000000..31f1b52 --- /dev/null +++ b/src/main/java/chess/domain/command/StatusCommand.java @@ -0,0 +1,13 @@ +package chess.domain.command; + +public class StatusCommand implements Command { + private static final String COMMAND = "status"; + + public static boolean isStatus(String operation) { + return COMMAND.equals(operation.toLowerCase()); + } + + @Override + public void execute() { + } +} diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java new file mode 100644 index 0000000..18f3958 --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class Bishop extends Piece { + private static final char representation = 'b'; + + public Bishop(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java new file mode 100644 index 0000000..879a8fd --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class King extends Piece { + private static final char representation = 'k'; + + public King(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java new file mode 100644 index 0000000..53d862d --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class Knight extends Piece { + private static final char representation = 'n'; + + public Knight(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/piece/Location.java b/src/main/java/chess/domain/piece/Location.java new file mode 100644 index 0000000..afc605c --- /dev/null +++ b/src/main/java/chess/domain/piece/Location.java @@ -0,0 +1,27 @@ +package chess.domain.piece; + +import java.util.Objects; + +public class Location { + private int y; + private int x; + + public Location(int y, int x) { + this.y = y; + this.x = x; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Location location = (Location) o; + return y == location.y && + x == location.x; + } + + @Override + public int hashCode() { + return Objects.hash(y, x); + } +} diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java new file mode 100644 index 0000000..f2bc59d --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class Pawn extends Piece { + private static final char representation = 'p'; + + public Pawn(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java new file mode 100644 index 0000000..9e4a392 --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,11 @@ +package chess.domain.piece; + +public class Piece { + private char representation; + private Location location; + + public Piece(char representation, Location location) { + this.representation = representation; + this.location = location; + } +} diff --git a/src/main/java/chess/domain/piece/Pieces.java b/src/main/java/chess/domain/piece/Pieces.java new file mode 100644 index 0000000..be0162b --- /dev/null +++ b/src/main/java/chess/domain/piece/Pieces.java @@ -0,0 +1,11 @@ +package chess.domain.piece; + +import java.util.List; + +public class Pieces { + private List pieces; + + public Pieces(List pieces) { + this.pieces = pieces; + } +} diff --git a/src/main/java/chess/domain/piece/PiecesCreator.java b/src/main/java/chess/domain/piece/PiecesCreator.java new file mode 100644 index 0000000..f1d72e6 --- /dev/null +++ b/src/main/java/chess/domain/piece/PiecesCreator.java @@ -0,0 +1,62 @@ +package chess.domain.piece; + +import chess.domain.player.Team; + +import java.util.ArrayList; +import java.util.List; + +public class PiecesCreator { + private static final int WHITE_PAWN_ROW = 6; + private static final int BLACK_PAWN_ROW = 1; + private static final int WHITE_NOBLE_ROW = 7; + private static final int BLACK_NOBLE_ROW = 0; + private static final int MAX_POSITION_OF_CHESS_BOARD = 8; + + private PiecesCreator() { + } + + + public static Pieces createPieces(Team team) { + List pieces = new ArrayList<>(); + addPawns(pieces, team); + addNobles(pieces, team); + return new Pieces(pieces); + } + + private static void addNobles(List pieces, Team team) { + int nobleRow = getNobleRow(team); + pieces.add(new Rook(new Location(nobleRow, 0))); + pieces.add(new Knight(new Location(nobleRow, 1))); + pieces.add(new Bishop(new Location(nobleRow, 2))); + pieces.add(new Queen(new Location(nobleRow, 3))); + pieces.add(new King(new Location(nobleRow, 4))); + pieces.add(new Bishop(new Location(nobleRow, 5))); + pieces.add(new Knight(new Location(nobleRow, 6))); + pieces.add(new Rook(new Location(nobleRow, 7))); + } + + private static void addPawns(List pieces, Team team) { + int pawnRow = getPawnRow(team); + for (int column = 0; column < MAX_POSITION_OF_CHESS_BOARD; column++) { + pieces.add(new Pawn(new Location(pawnRow, column))); + } + } + + private static int getNobleRow(Team team) { + if (isWhite(team)) { + return WHITE_NOBLE_ROW; + } + return BLACK_NOBLE_ROW; + } + + private static int getPawnRow(Team team) { + if (isWhite(team)) { + return WHITE_PAWN_ROW; + } + return BLACK_PAWN_ROW; + } + + private static boolean isWhite(Team team) { + return team == Team.WHITE; + } +} diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java new file mode 100644 index 0000000..9cd7f28 --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class Queen extends Piece { + private static final char representation = 'q'; + + public Queen(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java new file mode 100644 index 0000000..3a059f4 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,9 @@ +package chess.domain.piece; + +public class Rook extends Piece { + private static final char representation = 'r'; + + public Rook(Location location) { + super(representation, location); + } +} diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java new file mode 100644 index 0000000..35ceb83 --- /dev/null +++ b/src/main/java/chess/domain/player/Player.java @@ -0,0 +1,14 @@ +package chess.domain.player; + +import chess.domain.piece.Pieces; +import chess.domain.piece.PiecesCreator; + +public class Player { + private Team team; + private Pieces pieces; + + public Player(Team team) { + this.team = team; + this.pieces = PiecesCreator.createPieces(team); + } +} diff --git a/src/main/java/chess/domain/player/Team.java b/src/main/java/chess/domain/player/Team.java new file mode 100644 index 0000000..24e9ab5 --- /dev/null +++ b/src/main/java/chess/domain/player/Team.java @@ -0,0 +1,5 @@ +package chess.domain.player; + +public enum Team { + WHITE, BLACK +} diff --git a/src/main/java/chess/exceptions/NotSupportedCommandException.java b/src/main/java/chess/exceptions/NotSupportedCommandException.java new file mode 100644 index 0000000..da9b0e5 --- /dev/null +++ b/src/main/java/chess/exceptions/NotSupportedCommandException.java @@ -0,0 +1,9 @@ +package chess.exceptions; + +public class NotSupportedCommandException extends RuntimeException { + private static final String message = "지원하지 않는 커맨드입니다. "; + + public NotSupportedCommandException() { + super(message); + } +} diff --git a/src/main/java/chess/utils/LocationConverter.java b/src/main/java/chess/utils/LocationConverter.java new file mode 100644 index 0000000..08cc330 --- /dev/null +++ b/src/main/java/chess/utils/LocationConverter.java @@ -0,0 +1,27 @@ +package chess.utils; + +import chess.domain.piece.Location; + +public class LocationConverter { + private static final int INT_VALUE_OF_LOWER_A = 97; + private static final int SIZE_OF_CHESS_BOARD = 8; + private static final int FIRST_INDEX = 0; + private static final int SECOND_INDEX = 1; + + private LocationConverter() { + } + + public static Location convertLocation(String rawLocation) { + char[] splitLocation = rawLocation.toCharArray(); + return new Location(convertRow(splitLocation[SECOND_INDEX]), convertColumn(splitLocation[FIRST_INDEX])); + } + + private static int convertColumn(char rawColumn) { + char lowerRawRow = Character.toLowerCase(rawColumn); + return (int) (lowerRawRow) - INT_VALUE_OF_LOWER_A; + } + + private static int convertRow(char rawRow) { + return SIZE_OF_CHESS_BOARD - Character.getNumericValue(rawRow); + } +} diff --git a/src/main/java/chess/validators/CommandValidator.java b/src/main/java/chess/validators/CommandValidator.java new file mode 100644 index 0000000..f1dd743 --- /dev/null +++ b/src/main/java/chess/validators/CommandValidator.java @@ -0,0 +1,29 @@ +package chess.validators; + +import chess.exceptions.NotSupportedCommandException; + +import java.util.HashSet; +import java.util.Set; + +public class CommandValidator { + private static final Set commands = new HashSet<>(); + + static { + commands.add("start"); + commands.add("end"); + commands.add("move"); + commands.add("status"); + } + + public static void validateCommand(String rawCommand) { + String firstIndexOfCommand = getFirstIndex(rawCommand); + String lowercaseFirstIndex = firstIndexOfCommand.toLowerCase(); + if (!commands.contains(lowercaseFirstIndex)) { + throw new NotSupportedCommandException(); + } + } + + private static String getFirstIndex(String rawCommand) { + return rawCommand.split(" ")[0]; + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 0000000..b49d7e5 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,16 @@ +package chess.view; + +import java.util.Scanner; + +public class InputView { + private static final Scanner SCANNER = new Scanner(System.in); + private static final String INPUT_COMMAND = "명령어를 입력하세요. "; + + private InputView() { + } + + public static String getCommand() { + OutputView.printMessageAndNewLine(INPUT_COMMAND); + return SCANNER.nextLine(); + } +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 0000000..21cc8ce --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,17 @@ +package chess.view; + +public class OutputView { + private static final String GREETING_MESSAGE = "체스 게임을 시작합니다.\n게임 시작 : start\n게임 종료 : end\n" + + "게임 이동 : move source위치 - target위치 ex)move b2 b3"; + + private OutputView() { + } + + public static void printGreetingMessage() { + printMessageAndNewLine(GREETING_MESSAGE); + } + + public static void printMessageAndNewLine(String message) { + System.out.println(message); + } +} diff --git a/src/main/java/utils/CommandSplitter.java b/src/main/java/utils/CommandSplitter.java new file mode 100644 index 0000000..8c1874c --- /dev/null +++ b/src/main/java/utils/CommandSplitter.java @@ -0,0 +1,14 @@ +package utils; + +public class CommandSplitter { + private static final int SOURCE_INDEX = 1; + private static final int DESTINATION_INDEX = 2; + + private CommandSplitter() { + } + + public static String[] getSourceAndDest(String rawCommand) { + String[] splitCommand = rawCommand.split(" "); + return new String[]{splitCommand[SOURCE_INDEX], splitCommand[DESTINATION_INDEX]}; + } +} diff --git a/src/test/java/chess/utils/LocationConverterTest.java b/src/test/java/chess/utils/LocationConverterTest.java new file mode 100644 index 0000000..41adaaa --- /dev/null +++ b/src/test/java/chess/utils/LocationConverterTest.java @@ -0,0 +1,20 @@ +package chess.utils; + +import chess.domain.piece.Location; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LocationConverterTest { + @Test + public void 위치변환기_동작_테스트() { + //given + String rawLocation = "a2"; + + //when + Location convertedLocation = LocationConverter.convertLocation(rawLocation); + + //then + assertThat(convertedLocation).isEqualTo(new Location(6, 0)); + } +} \ No newline at end of file