Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
30b8b60
docs: 기능 목록 및 추후 구현 목록 작성
kjyyjk Mar 22, 2025
f0595c8
feat: Piece 추상 부모 클래스 생성
kjyyjk Mar 22, 2025
b448b11
feat: 킹은 원하는 방향으로 한칸 이동 가능하다
kjyyjk Mar 22, 2025
4ec0a00
feat: 각 기물 인스턴스는 팀을 의미하는 색깔을 가진다.
kjyyjk Mar 22, 2025
59a5869
feat: King이 같은 팀을 잡으려고 할 경우 예외를 발생한다
kjyyjk Mar 22, 2025
44a4dfa
feat: Bishop의 이동 가능한 경우의 수를 초기화한다
kjyyjk Mar 22, 2025
7bf7bb7
feat: Bishop이 이동 불가능한 경우에 예외를 발생한다
kjyyjk Mar 22, 2025
ade188b
feat: 기물의 움직임을 체크하는 공통 로직을 Piece 상위 클래스에 구현한다.
kjyyjk Mar 22, 2025
65f7304
feat: Knight의 이동 가능한 경우의 수를 초기화한다
kjyyjk Mar 22, 2025
9837c63
feat: Queen의 이동 가능한 경우의 수를 초기화한다
kjyyjk Mar 22, 2025
07d81a8
feat: Rook의 이동 가능한 경우의 수를 초기화한다
kjyyjk Mar 22, 2025
1f4591a
feat: Pawn의 이동 가능한 경우의 수를 초기화한다
kjyyjk Mar 22, 2025
fcd8140
feat: 처음 움직이는 Pawn만 앞으로 두 칸까지 이동할 수 있다
kjyyjk Mar 22, 2025
0a8cb86
feat: 체스판을 초기화한다
kjyyjk Mar 22, 2025
4e7ead9
test: 경로 중간에 기물이 존재하면 예외를 발생한다
kjyyjk Mar 22, 2025
c22dd29
test: 같은 색깔의 기물을 잡으려고 하면 예외를 발생한다
kjyyjk Mar 22, 2025
0637b6f
test: Knight는 중간 기물을 뛰어넘을 수 있다
kjyyjk Mar 22, 2025
4c54227
test: 이미 움직인 Pawn이 앞으로 두칸 이동 시 예외를 발생한다
kjyyjk Mar 22, 2025
54f963e
test: Knight의 정상적인 이동을 테스트한다
kjyyjk Mar 22, 2025
2c2aab5
feat: 사용자가 턴을 돌며 이동을 입력한다
kjyyjk Mar 22, 2025
b12eb80
feat: 흑색 진영 폰은 흰색 진영과 반대로 움직인다
kjyyjk Mar 22, 2025
f682254
feat: 매 사용자 입력마다 보드 판을 출력한다
kjyyjk Mar 22, 2025
f6ddb19
feat: 흑색 진영 폰도 처음 이동이 아니면 아래로 두칸 이동할 수 없다
kjyyjk Mar 22, 2025
5280880
feat: 폰은 대각 위치에 상대 기물이 존재하는 경우에만 대각으로 움직일 수 있다
kjyyjk Mar 22, 2025
0cac3bd
docs: 기능 목록 최신화
kjyyjk Mar 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,39 @@
## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

### 기능 목록
- [x] 초기 보드판을 구성한다.
- [x] 플레이어가 턴을 주고 받는다.
- [x] 플레이어는 자신의 턴에 기물을 움직인다.
- [x] 킹
- 어느 방향이든 위/아래/양옆/대각선을 한 칸씩 이동할 수 있다.
- [x] 퀸
- 일직선으로 어떤 방향이든 원하는 만큼 이동할 수 있다.
- [x] 룩
- 일직선으로 앞/뒤/양옆으로 원하는 만큼 이동할 수 있다.
- [x] 비숍
- 일직선으로 대각선을 원하는 만큼 이동할 수 있다.
- [x] 나이트
- 한 방향으로 두칸을 이동하고 그와 90도를 이루는 방향으로 한칸 이동한다.(L자 모양)
- 진행 경로에 다른 기물이 있어도 가로 막히지 않는다.
- [x] 폰
- [x] 처음 움직이는 폰은 앞으로 두 칸까지 이동할 수 있다.
- [x] 이후부터는 앞으로 한 칸씩만 이동할 수 있다.
- [x] 잡을 때는 대각선으로 한 칸 앞에 있는 상대 기물만 잡을 수 있다.
- [x] 흑색 진영은 흰색 진영과 반대로 이동한다.
- 이동 규칙
- [x] 나이트를 제외한 모든 기물은 다른 기물을 뛰어 넘을 수 없다.
- [x] 상대편의 말만 잡을 수 있다.
- [x] 입력한 곳으로 이동할 수 없는 경우 예외를 발생한다.


### 추후 구현 목록
- 킹은 스스로 체크 위치로 이동할 수 없다.
- 폰을 승진할 수 있다.
- 앙 파상 규칙을 적용한다.
- 캐슬링 규칙을 적용한다.
- 킹이 잡히면 게임을 종료한다.
- 체크 메이트 규칙을 적용한다.
- 게임을 끝낼 수 없는 경우 무승부처리한다.
- 킹이 잡히면 잡힌 쪽이 패배하며 게임을 종료한다
109 changes: 109 additions & 0 deletions src/main/java/chess/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package chess;

import static chess.Color.*;
import static chess.Column.*;
import static chess.Row.*;

import chess.board.Board;
import chess.piece.Bishop;
import chess.piece.King;
import chess.piece.Knight;
import chess.piece.Pawn;
import chess.piece.Piece;
import chess.piece.Queen;
import chess.piece.Rook;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Application {
private static final Scanner scanner = new Scanner(System.in);

public static void main(String[] args) {
Board board = new Board(makeInitialBoard());
System.out.println("체스 게임을 시작합니다.");
Color currentColor = WHITE;
printCurrentBoard(board);
while(true) {
System.out.printf("\n%s 차례입니다.\n움직이려는 기물의 좌표와 도착 좌표를 입력하세요.\n", currentColor);
playTurn(board, currentColor);
printCurrentBoard(board);
currentColor = currentColor.opposite();
}
}

private static void playTurn(Board board, Color currentColor) {
String input = scanner.nextLine();
String[] split = input.split(" ");
String[] startString = split[0].split("");
String[] goalString = split[1].split("");
Position start = createPosition(startString);
Position goal = createPosition(goalString);
try {
board.move(currentColor, start, goal);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
System.out.println("다시 입력하세요");
playTurn(board, currentColor);
}
}

private static void printCurrentBoard(Board currentBoard) {
Map<Position, Piece> board = currentBoard.getBoard();
System.out.println();
for (Row row : Row.values()) {
System.out.print(row.getValue());
for (Column column : Column.values()) {
Position position = new Position(row, column);
Piece piece = board.get(position);
if (piece == null) {
System.out.print("ㅁ");
continue;
}
System.out.print(piece);
}
System.out.println();
}
System.out.println(" A");
}

public static Position createPosition(String[] positionString) {
Column column = getColumn(positionString[0]);
Row row = getRow(positionString[1]);
return new Position(row, column);
}

private static Map<Position, Piece> makeInitialBoard() {
Map<Position, Piece> board = new HashMap<>();
initPawns(board);
board.put(new Position(ONE, A), new Rook(WHITE));
board.put(new Position(ONE, H), new Rook(WHITE));
board.put(new Position(EIGHT, A), new Rook(BLACK));
board.put(new Position(EIGHT, H), new Rook(BLACK));

board.put(new Position(ONE, B), new Knight(WHITE));
board.put(new Position(ONE, G), new Knight(WHITE));
board.put(new Position(EIGHT, B), new Knight(BLACK));
board.put(new Position(EIGHT, G), new Knight(BLACK));

board.put(new Position(ONE, C), new Bishop(WHITE));
board.put(new Position(ONE, F), new Bishop(WHITE));
board.put(new Position(EIGHT, C), new Bishop(BLACK));
board.put(new Position(EIGHT, F), new Bishop(BLACK));

board.put(new Position(ONE, D), new Queen(WHITE));
board.put(new Position(EIGHT, D), new Queen(BLACK));

board.put(new Position(ONE, E), new King(WHITE));
board.put(new Position(EIGHT, E), new King(BLACK));

return board;
}

private static void initPawns(Map<Position, Piece> board) {
for (Column column : Column.values()) {
board.put(new Position(TWO, column), new Pawn(WHITE));
board.put(new Position(SEVEN, column), new Pawn(BLACK));
}
}
}
31 changes: 23 additions & 8 deletions src/main/java/chess/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@

public enum Column {

A,
B,
C,
D,
E,
F,
G,
H;
A("A"),
B("B"),
C("C"),
D("D"),
E("E"),
F("F"),
G("G"),
H("H");

private final String value;

Column(String value) {
this.value = value;
}

public static Column getColumn(String value) {
for (Column column : values()) {
if (column.value.equals(value)) {
return column;
}
}
throw new IllegalArgumentException("유효하지 않은 Column입니다.");
}

public boolean isFarLeft() {
return ordinal() == 0;
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/chess/Position.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package chess;

import java.util.Objects;

public record Position(
Column column,
Row row
Expand Down Expand Up @@ -167,4 +169,22 @@ public Position moveHorizontal(final int step) {
}
return this;
}

@Override
public boolean equals(Object object) {
if (object == null || getClass() != object.getClass()) {
return false;
}
Position position = (Position) object;
return row == position.row && column == position.column;
}

@Override
public int hashCode() {
return Objects.hash(column, row);
}

public boolean canNotMove(Movement movement) {
return !canMove(movement);
}
}
46 changes: 38 additions & 8 deletions src/main/java/chess/Row.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
package chess;

import java.util.InputMismatchException;

public enum Row {

EIGHT,
SEVEN,
SIX,
FIVE,
FOUR,
THREE,
TWO,
ONE;
EIGHT(8),
SEVEN(7),
SIX(6),
FIVE(5),
FOUR(4),
THREE(3),
TWO(2),
ONE(1);

private final int value;

Row(int value) {
this.value = value;
}

public static Row getRow(String value) {
try {
int intValue = Integer.parseInt(value);
return getRow(intValue);
} catch(NumberFormatException e) {
throw new IllegalArgumentException("유효하지 않은 Row입니다.");
}
}

public static Row getRow(int value) {
for (Row row : values()) {
if (row.value == value) {
return row;
}
}
throw new IllegalArgumentException("유효하지 않은 Row입니다.");
}

public int getValue() {
return value;
}

public boolean isTop() {
return ordinal() == 0;
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/chess/board/Board.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package chess.board;

import chess.Color;
import chess.Column;
import chess.Position;
import chess.piece.Piece;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class Board {
private final Map<Position, Piece> board;

public Board(Map<Position, Piece> board) {
this.board = new HashMap<>(board);
}

public void move(Color color, Position start, Position goal) {
Piece piece = board.get(start);
if (piece == null) {
throw new IllegalArgumentException("해당 위치에 기물이 존재하지 않습니다.");
}
if (piece.isDifferentColor(color)) {
throw new IllegalArgumentException("다른 팀의 기물을 움직일 수 없습니다.");
}
piece.validateMovable(this, start, goal);
board.put(goal, piece);
board.remove(start);
}

public boolean isSameColorPieceExists(Position goal, Color color) {
Piece piece = board.get(goal);
if (piece == null) {
return false;
}
return piece.isSameColor(color);
}

public boolean isDifferentColorPieceNotExists(Position start, Position goal) {
Piece startPiece = board.get(start);
Piece goalPiece = board.get(goal);
if (startPiece == null || goalPiece == null) {
return true;
}
return startPiece.isSameColor(goalPiece);
}

public boolean isPieceExists(Position position) {
Piece piece = board.get(position);
return piece != null;
}

public Map<Position, Piece> getBoard() {
return Collections.unmodifiableMap(board);
}
}
65 changes: 64 additions & 1 deletion src/main/java/chess/piece/Bishop.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
package chess.piece;

public class Bishop {
import static chess.Movement.LEFT_DOWN;
import static chess.Movement.LEFT_UP;
import static chess.Movement.RIGHT_DOWN;
import static chess.Movement.RIGHT_UP;

import chess.Color;
import chess.Movement;
import chess.Position;
import chess.board.Board;
import java.util.ArrayList;
import java.util.List;

public class Bishop extends Piece {

public static List<List<Movement>> allMovements = new ArrayList<>();

static {
for (Movement movement : List.of(LEFT_UP, RIGHT_UP, RIGHT_DOWN, LEFT_DOWN)) {
for (int i = 1; i < 8; i++) {
List<Movement> movements = new ArrayList<>();
for (int j = 0; j < i; j++) {
movements.add(movement);
}
allMovements.add(movements);
}
}
}

public Bishop(final Color color) {
super(color);
}

protected void validateMiddlePath(Board board, List<Position> root) {
root.removeFirst();
root.removeLast();
for (Position position : root) {
if (board.isPieceExists(position)) {
throw new IllegalArgumentException("중간에 다른 기물을 뛰어넘을 수 없습니다.");
}
}
}

protected List<Position> findRoot(Board board, Position start, Position goal) {
for (List<Movement> movements : allMovements) {
List<Position> root = new ArrayList<>();
Position now = start;
root.add(now);
for (Movement movement : movements) {
if (now.canNotMove(movement)) {
break;
}
now = now.move(movement);
root.add(now);
}
if (now.equals(goal)) {
return root;
}
}
throw new IllegalArgumentException("해당 기물은 해당 위치로 이동할 수 없습니다.");
}

@Override
public String toString() {
return "숍";
}
}
Loading