Skip to content

[1,2,3단계 - 체스] 현구막(최현구) 미션 제출합니다. #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 72 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f61dace
docs: README.md 기능 목록 작성
sjpark-dev Mar 16, 2021
d818954
feat: Xpoint, Ypoint 구현
sjpark-dev Mar 16, 2021
cfa7661
feat: 좌표 구현 및 캐싱
sjpark-dev Mar 16, 2021
36c1564
feat: 체스말 구현
sjpark-dev Mar 17, 2021
9a35713
feat: 체스판 좌표 구현
sjpark-dev Mar 17, 2021
f506355
feat: 체스게임 초기 상태 출력 구현
sjpark-dev Mar 17, 2021
bfd5b25
refactor: 체스게임 초기 상태 출력 로직 리펙터링
sjpark-dev Mar 18, 2021
e6e3712
refactor: 체스말의 상태 리펙터링
sjpark-dev Mar 18, 2021
99750f4
feat: 좌표 이동 구현
sjpark-dev Mar 19, 2021
568f0f8
refactor: 좌표 경계 이동 로직 리펙터링
sjpark-dev Mar 20, 2021
bf34bd5
refactor: 생성 로직 수정
sjpark-dev Mar 20, 2021
0db8f46
feat: 폰과 룩의 이동 로직 구현
sjpark-dev Mar 20, 2021
365b747
feat: 좌표에 해당하는 체스말 찾아오기 구현
sjpark-dev Mar 20, 2021
0823c1c
feat: 체스말 이동 전략 일부 구현
sjpark-dev Mar 20, 2021
001630d
refactor: 사용되지 않는 import 제거
sjpark-dev Mar 20, 2021
8e9400f
feat: 게임 상태 구현
sjpark-dev Mar 21, 2021
39f6e90
feat: 이동 전략 구현
sjpark-dev Mar 21, 2021
1a0406a
refactor: CommonMoveStrategy 테스트 추가 및 주석 처리
sjpark-dev Mar 21, 2021
afb789d
refactor: 커맨드 기능 구현
sjpark-dev Mar 21, 2021
f5cb5a0
fix: 킹, 퀸, 비숍, 룩 이동 전략 수정
sjpark-dev Mar 21, 2021
134e476
refactor 커맨드 기능 리펙터링
sjpark-dev Mar 21, 2021
0a9a1e5
refactor: 상태 전환 기능 리펙터링
sjpark-dev Mar 21, 2021
69b9c72
feat: move 기능 구현
sjpark-dev Mar 22, 2021
7807517
feat: 점수 계산 객체 구현
sjpark-dev Mar 22, 2021
2af11aa
refactor: 턴 기능 리펙토링
sjpark-dev Mar 22, 2021
7e57ce7
refactor: 턴 테스트 수정
sjpark-dev Mar 22, 2021
52ca1e0
refactor: 이동 전략 기능 리펙터링
sjpark-dev Mar 22, 2021
fbea316
fix: 폰 이동 전략 수정
sjpark-dev Mar 22, 2021
389baa5
refactor: 상태 전환 기능 리펙터링
sjpark-dev Mar 22, 2021
c78a5fc
refactor: 턴 상태 Test 리펙터링
sjpark-dev Mar 22, 2021
272a9f0
feat: 점수 기능 구현
sjpark-dev Mar 22, 2021
0ed6725
fix: 상태 전환 버그 수정
sjpark-dev Mar 22, 2021
6368cdc
refactor: move package classes about piece, delete unused class
Hyeon9mak Mar 24, 2021
51cf33a
feat: create new object to horizental of chess board
Hyeon9mak Mar 25, 2021
dc2d6a3
refactor: rename find piece method in Board class
Hyeon9mak Mar 25, 2021
e70940b
refactor: fix check alive both kings method
Hyeon9mak Mar 25, 2021
9fb8074
refactor: fix move position of piece method
Hyeon9mak Mar 25, 2021
136388d
refactor: rename variable 'findedPiece' to 'foundPiece'
Hyeon9mak Mar 25, 2021
f4f804d
refactor: rename variable 'findedPiece' to 'foundPiece'
Hyeon9mak Mar 25, 2021
5dc2637
refactor: fix init board logic, add init board constructor in Board
Hyeon9mak Mar 25, 2021
59bf8b8
refactor: delete unuseful try-catch keyword in Commands
Hyeon9mak Mar 25, 2021
3ec4c3f
refactor: fix piece move strategy test methods
Hyeon9mak Mar 25, 2021
0ebdcd4
refactor: delete unused methods
Hyeon9mak Mar 25, 2021
2d9096d
refactor: add exception message
Hyeon9mak Mar 25, 2021
e42f641
refactor: fix print message format
Hyeon9mak Mar 25, 2021
6666d6e
style: fix enter line
Hyeon9mak Mar 25, 2021
b6d2f09
refactor: rename object from InitPieces to InitPosition
Hyeon9mak Mar 26, 2021
15bbfd9
refactor: add list index const variable, fix stream filter method
Hyeon9mak Mar 26, 2021
9097e6f
refactor: add boolean method for delete negation operator
Hyeon9mak Mar 26, 2021
40eaedb
refactor: rename Rank getter method, fix negative operator method
Hyeon9mak Mar 26, 2021
971c7eb
refactor: fix ScoreCalculator object
Hyeon9mak Mar 26, 2021
4405c26
chore: merge remote-tracking branch 'origin/step1' into step1
Hyeon9mak Mar 26, 2021
052cef9
refactor: add new command enum class
Hyeon9mak Mar 26, 2021
45bc602
refactor: rollback command pattern, add Finish chess command
Hyeon9mak Mar 26, 2021
b131296
refactor: fix Init state
Hyeon9mak Mar 26, 2021
559c6bc
refactor: fix WhiteTurn state
Hyeon9mak Mar 26, 2021
df01b21
refactor: fix BlackTurn state
Hyeon9mak Mar 26, 2021
5c2c162
refactor: fix WhiteWin state
Hyeon9mak Mar 26, 2021
f8e94a7
refactor: fix BlackWin state
Hyeon9mak Mar 26, 2021
01de0a5
refactor: fix End state
Hyeon9mak Mar 26, 2021
63fcd3d
refactor: fix Running abstract class
Hyeon9mak Mar 26, 2021
976054d
refactor: fix Finished abstract class
Hyeon9mak Mar 26, 2021
6634acc
refactor: fix AfterStart abstract class
Hyeon9mak Mar 26, 2021
207464d
refactor: add compare kings count logic
Hyeon9mak Mar 26, 2021
9546136
refactor: fix ChessGame based on state refactoring
Hyeon9mak Mar 26, 2021
ef41c8b
refactor: delete unused method
Hyeon9mak Mar 26, 2021
bc23631
refactor: fix Empty piece move stractegy
Hyeon9mak Mar 26, 2021
f88d2b5
feat: add Ypoint name getter method
Hyeon9mak Mar 26, 2021
2708ea1
refactor: fix DtoAssembler stream methods
Hyeon9mak Mar 26, 2021
c47376f
feat: add get winner method in Status command
Hyeon9mak Mar 26, 2021
7ee9e09
refactor: fix Chess Controller, OutputView
Hyeon9mak Mar 26, 2021
12bc658
style: fix enter-lines
Hyeon9mak Mar 26, 2021
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
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,64 @@
# java-chess
체스 게임 구현을 위한 저장소

<br>

## 리팩토링 구상 목록
- ~~Board를 출력을 위해 좌표를 정렬해둔 객체로만 사용하고, Piece들의 일급 컬렉션을~~
- List<Map<Position, Piece>> 로 이루어진 Board의 부분을 컬렉션 객체로 감싸기 (Rank)

<br>

## 기능 목록
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실행하다보니 다음과 같은 경우가 있네요.

  • 말이 없는 위치를 지정해서 move하는 경우 (move a1 a3에서 a1에 말이 없는 경우) 메시지가 null로 찍힘
  • 게임 중 end로 종료 시 한번에 종료되지 않음 (두번 입력해야 종료)


- [x] 체스판을 초기화한다.
- [x] 체스판의 x, y 좌표를 나눈다.
- [x] 좌표를 캐싱한다.
- [x] 좌표를 감싼 체스판을 만든다.
- [x] 체스말을 만든다.
- [x] 폰을 만든다.
- [x] 폰의 이동로직 '수직 한 칸'
- [x] 폰의 초기 이동로직 '수직 두 칸'
- [x] 룩을 만든다.
- [x] 룩의 이동로직 '수직수평 무한'
- [x] 나이트를 만든다.
- [x] 나이트의 이동로직 '수직수평 두 칸 후 대각선 한 칸'
- [x] 비숍을 만든다.
- [x] 비숍의 이동로직 '대각선 무한'
- [x] 퀸을 만든다.
- [x] 퀸의 이동로직 '수직수평 대각선 무한'
- [x] 킹을 만든다.
- [x] 킹의 이동로직 '수직수평 대각선 한 칸'
- [x] 좌표를 채우기 위한 빈말
- [x] 체스말들을 체스판 좌표에 배치시킨다.
- [x] 체스말을 이동시킨다.
- [x] source 좌표에 체스말이 존재하는지 확인한다.
- 좌표가 체스판에 존재하는지
- Empty 체스말을 둔 경우, 유효한 체스말인지 확인
- [x] 체스말이 없는 빈 좌표인 경우... (이동불가)
- [x] 적군 체스말이 존재하는 경우... (이동불가)
- [x] 체스판을 벗어나는 좌표인 경우...(이동불가)
- [x] 아군 체스말이 존재하는 경우... (이동가능 + target 좌표 검사 진행)
- [x] target 좌표에 체스말이 존재하는지 확인한다.
- [x] 아군 체스말이 존재하는 경우... (이동불가)
- [x] 체스판을 벗어나는 좌표인 경우...(이동불가)
- [x] 체스말이 없는 빈 좌표인 경우... (이동가능)
- [x] 적군 체스말이 존재하는 경우... (이동가능 + 적군 체스말을 제거하는 추가동작)
- 1. 내가 잡아먹은 체스 말이 '킹'인지 검사하는 방법
- 2. 현재 체스판에 '킹'이 2개 존재하는지 검사하는 방법
- [x] 승패 및 점수를 계산한다
- [x] 'end' 가 입력되는 경우 게임을 종료한다.
- [x] 킹이 잡히는 경우 게임을 종료한다.
- [x] 체스판에 남아있는 체스말의 점수를 총합한다
- [x] 폰은 1점으로 계산한다.
- [x] 같은 색의 폰이 세로줄에 있는 경우 해당 폰들의 점수를 0.5로 계산한다.
- [x] 룩은 5점으로 계산한다.
- [x] 나이트는 2.5점으로 계산한다.
- [x] 비숍은 3점으로 계산한다.
- [x] 퀸은 9점으로 계산한다.
- [x] 킹은 0점으로 계산한다.

<br>

## 질문 목록
- ~~InitBoardGenerator의 역할을 InitPieces에 통합하는게 어떤지?~~
18 changes: 9 additions & 9 deletions src/main/java/chess/WebUIChessApplication.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package chess;

import chess.controller.ChessController;
import java.util.Map;
import spark.ModelAndView;
import spark.template.handlebars.HandlebarsTemplateEngine;

import java.util.HashMap;
import java.util.Map;

import static spark.Spark.get;

public class WebUIChessApplication {

public static void main(String[] args) {
get("/", (req, res) -> {
Map<String, Object> model = new HashMap<>();
return render(model, "index.html");
});
// get("/", (req, res) -> {
// Map<String, Object> model = new HashMap<>();
// return render(model, "index.html");
// });
ChessController chessController = new ChessController();
chessController.run();
}

private static String render(Map<String, Object> model, String templatePath) {
Expand Down
67 changes: 67 additions & 0 deletions src/main/java/chess/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package chess.controller;

import chess.domain.board.Board;
import chess.domain.command.Command;
import chess.domain.command.Commands;
import chess.domain.command.Status;
import chess.domain.game.ChessGame;
import chess.utils.DtoAssembler;
import chess.view.InputView;
import chess.view.OutputView;

public class ChessController {

public void run() {
ChessGame chessGame = new ChessGame(new Board());
OutputView.printCommandNotice();

while (chessGame.isNotEnd()) {
chessGame = new ChessGame(new Board());
Commands commands = Commands.initCommands(chessGame);
readyChess(chessGame, commands);
runningChess(chessGame, commands);
finishedChess(chessGame, commands);
}
}

private void readyChess(ChessGame chessGame, Commands commands) {
OutputView.printRequestInputStart();
while (chessGame.isInit()) {
executeCommand(commands);
}
}

private void runningChess(ChessGame chessGame, Commands commands) {
OutputView.printChessStarted();
while (chessGame.isRunning()) {
OutputView.printChessBoard(DtoAssembler.board(chessGame.ranks()));
executeCommand(commands);
}
}

private void finishedChess(ChessGame chessGame, Commands commands) {
OutputView.printFinishWithReason(chessGame.winner());
while (chessGame.isFinished()) {
executeCommand(commands);
}
}

private void executeCommand(final Commands commands) {
try {
String input = InputView.command();
Command command = commands.matchedCommand(input);
command.execution(input);
printStatus(command);
} catch (IllegalStateException | IllegalArgumentException e) {
OutputView.printErrorException(e.getMessage());
}
}

private void printStatus(Command command) {
if (command.isStatus()) {
Status status = (Status) command;
OutputView.printWinner(status.winner());
OutputView.printScoreStatus(status.totalWhiteScore(), status.totalBlackScore());
}
}
}
74 changes: 74 additions & 0 deletions src/main/java/chess/domain/board/Board.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package chess.domain.board;

import chess.domain.board.position.InitPosition;
import chess.domain.board.position.Position;
import chess.domain.movestrategy.MoveStrategy;
import chess.domain.piece.Empty;
import chess.domain.piece.Piece;
import java.util.List;
import java.util.Set;

public class Board {

public static final int BOTH_KINGS_ALIVE = 2;
public static final String INVALID_POSITION_MESSAGE = "유효하지 않은 좌표 입력입니다.";

private final List<Rank> ranks;

public Board() {
this(InitPosition.initRanks());
}

public Board(final List<Rank> ranks) {
this.ranks = ranks;
}

public List<Rank> ranks() {
return this.ranks;
}

public Piece pieceByPosition(Position position) {
return this.ranks.stream()
.filter(rank -> rank.hasPosition(position))
.map(map -> map.piece(position))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(INVALID_POSITION_MESSAGE));
}

public boolean isAliveBothKings() {
return this.ranks.stream()
.flatMap(rank -> rank.pieces().stream())
.filter(Piece::isKing)
.count() == BOTH_KINGS_ALIVE;
}

public void moveIfValidPosition(Position source, Position target) {
if (isInvalidPosition(source, target)) {
throw new IllegalArgumentException(INVALID_POSITION_MESSAGE);
}

swapPieces(source, target);
}

private boolean isInvalidPosition(Position source, Position target) {
Piece piece = pieceByPosition(source);
MoveStrategy moveStrategy = piece.moveStrategy();
Set<Position> movablePath = moveStrategy.moveStrategy(this, source);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

더 나은 메서드명은 없을까요? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바로 위에 있는 메서드에서 valid라는 표현을 사용해서, 이 메서드도 isInvalid 표현을 사용하게 바꿨어요! 👍

return !movablePath.contains(target);
}

private void swapPieces(Position source, Position target) {
Piece sourcePiece = pieceByPosition(source);
replacePiece(source, Empty.create());
replacePiece(target, sourcePiece);
}

private void replacePiece(Position position, Piece piece) {
Rank foundRank = this.ranks.stream()
.filter(rank -> rank.hasPosition(position))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(INVALID_POSITION_MESSAGE));

foundRank.replacePiece(position, piece);
}
}
36 changes: 36 additions & 0 deletions src/main/java/chess/domain/board/Rank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package chess.domain.board;

import chess.domain.board.position.Position;
import chess.domain.piece.Piece;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Rank {

private final Map<Position, Piece> squares;

public Rank(final Map<Position, Piece> squares) {
this.squares = squares;
}

public boolean hasPosition(final Position position) {
return this.squares.containsKey(position);
}

public void replacePiece(final Position position, final Piece piece) {
this.squares.replace(position, piece);
}

public Piece piece(Position position) {
return this.squares.get(position);
}

public List<Piece> pieces() {
return new ArrayList<>(this.squares.values());
}

public Map<Position, Piece> squares() {
return this.squares;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일급컬렉션에서는 담당한 컬렉션을 외부로 반환해야할 때, 외부에서 조작할 수 있으므로 항상 주의해야 합니다.

  • 반환하지 않을 수 있다면 가장 Best
  • 반환해야만 한다면 불변으로, 혹은 현재 필드와 관계없는 새로운 컬렉션으로 만들어서 반환

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일급컬렉션을 일급컬렉션답게 사용하지 못하고 있었네요 😢
squares를 요청하는 곳에서 Rank에게 메세지를 보낼 수 있는 방법에 대해 고민해보고 적용해보겠습니다!

}
}
70 changes: 70 additions & 0 deletions src/main/java/chess/domain/board/position/InitPosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package chess.domain.board.position;

import chess.domain.board.Rank;
import chess.domain.piece.Bishop;
import chess.domain.piece.Empty;
import chess.domain.piece.King;
import chess.domain.piece.Knight;
import chess.domain.piece.Pawn;
import chess.domain.piece.Piece;
import chess.domain.piece.Queen;
import chess.domain.piece.Rook;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public enum InitPosition {
BLACK_ROOK(Xpoint.getRookPoints(), Ypoint.getBlackPoint(), Rook.createBlack()),
WHITE_ROOK(Xpoint.getRookPoints(), Ypoint.getWhitePoint(), Rook.createWhite()),
BLACK_KNIGHT(Xpoint.getKnightPoints(), Ypoint.getBlackPoint(), Knight.createBlack()),
WHITE_KNIGHT(Xpoint.getKnightPoints(), Ypoint.getWhitePoint(), Knight.createWhite()),
BLACK_BISHOP(Xpoint.getBishopPoints(), Ypoint.getBlackPoint(), Bishop.createBlack()),
WHITE_BISHOP(Xpoint.getBishopPoints(), Ypoint.getWhitePoint(), Bishop.createWhite()),
BLACK_QUEEN(Xpoint.getQueenPoint(), Ypoint.getBlackPoint(), Queen.createBlack()),
WHITE_QUEEN(Xpoint.getQueenPoint(), Ypoint.getWhitePoint(), Queen.createWhite()),
BLACK_KING(Xpoint.getKingPoint(), Ypoint.getBlackPoint(), King.createBlack()),
WHITE_KING(Xpoint.getKingPoint(), Ypoint.getWhitePoint(), King.createWhite()),
BLACK_PAWN(Xpoint.getPawnOrEmptyPoints(), Ypoint.getBlackPawnPoint(), Pawn.createBlack()),
WHITE_PAWN(Xpoint.getPawnOrEmptyPoints(), Ypoint.getWhitePawnPoint(), Pawn.createWhite()),
EMPTY_PIECE(Xpoint.getPawnOrEmptyPoints(), Ypoint.getEmptyPoints(), Empty.create());

private final List<Xpoint> xPoints;
private final List<Ypoint> yPoints;
private final Piece piece;

InitPosition(final List<Xpoint> xPoints, final List<Ypoint> yPoints, final Piece piece) {
this.xPoints = xPoints;
this.yPoints = yPoints;
this.piece = piece;
}

public static List<Rank> initRanks() {
List<Rank> ranks = new ArrayList<>();
for (Ypoint ypoint : Ypoint.values()) {
ranks.add(initRank(ypoint));
}
return ranks;
}

private static Rank initRank(final Ypoint ypoint) {
Map<Position, Piece> line = new LinkedHashMap<>();
for (Xpoint xpoint : Xpoint.values()) {
line.put(Position.of(xpoint, ypoint), InitPosition.findPiece(xpoint, ypoint));
}
return new Rank(line);
}

private static Piece findPiece(final Xpoint xpoint, final Ypoint ypoint) {
return Arrays.stream(values())
.filter(p -> p.containsXY(xpoint, ypoint))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("해당좌표의 체스말을 찾지 못했습니다."))
.piece;
}

private boolean containsXY(final Xpoint xpoint, final Ypoint ypoint) {
return this.xPoints.contains(xpoint) && this.yPoints.contains(ypoint);
}
}
Loading