Skip to content

폴로 체스 #1

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

Open
wants to merge 56 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
b9bfa0f
[1,2단계 - 체스] 폴로(이동훈) 미션 제출합니다. (#432)
green-kong Mar 21, 2023
e60627a
feat: King이 잡혔는지 확인하는 기능 구현
green-kong Mar 22, 2023
858baaa
feat: King이 잡히면 게임을 종료하는 기능
green-kong Mar 22, 2023
4604288
refa: ChessBoard 생성자 Map 주입받는 방식으로 변경
green-kong Mar 22, 2023
845fa11
refa: Piece에서 필요없는 메서드 삭제
green-kong Mar 22, 2023
3db49d6
feat: 기물마다 해당하는 점수를 반환하는 기능
green-kong Mar 22, 2023
18458c3
feat: camp를 인자로 전달받아 해당하는 캠프의 몇개의 폰이 같은 랭크에 있는지 카운트하여 반환하는 기능
green-kong Mar 22, 2023
61f7c6d
refa: ChessBoard#countPawnInAllColumns stream으로 리팩토링
green-kong Mar 22, 2023
994d478
feat: camp마다 기물의 점수 총합을 구하는 기능 구현
green-kong Mar 22, 2023
c54afef
feat: 점수계산을 위한 Score 클래스 생성
green-kong Mar 22, 2023
cff90d7
feat: 같은 파일에 존재하는 폰의 갯수를 전달 받아 점수를 계산하는 기능 구현
green-kong Mar 22, 2023
066e8b6
feat: pawn 패널티를 적용한 최종 스코어를 계산하는 기능
green-kong Mar 22, 2023
47f4510
feat: 팀별 점수를 받아 출력하는 기능 구현
green-kong Mar 22, 2023
658521c
refa: 코딩컨벤션
green-kong Mar 22, 2023
0005b53
feat: docker mysql 셋팅
green-kong Mar 22, 2023
1105b54
feat: jdbc connection
green-kong Mar 23, 2023
15c506e
fix: table character set 변경
green-kong Mar 23, 2023
335dc8c
feat: 게임방 생성및 전체 목록 불러오는 기능 구현
green-kong Mar 23, 2023
cdd0afb
feat: moveHistory를 db에 저장하는 기능 구현
green-kong Mar 23, 2023
07229b0
feat: connector 를 이용하여 test db와 product db 분리
green-kong Mar 23, 2023
0866a36
feat: board를 저장하고 불러오는 기능 구현
green-kong Mar 23, 2023
3386641
fix: board table camp varchar로 변경
green-kong Mar 23, 2023
2bb38bd
fix: addGame 후 생성된 row의 pk 반환하도록 변경
green-kong Mar 23, 2023
a6eee0e
feat: 게임방 생성하는 기능 구현
green-kong Mar 23, 2023
2bdd89d
fix: 게임이름으로 게임룸 id를 반환하는 기능 복구
green-kong Mar 23, 2023
852d42f
refa: Piece 생성시 생성자로 type 받지 않게 변경
green-kong Mar 23, 2023
2200aaf
feat: type 에서 camp를 통해 piece 생성하는 기능 구현
green-kong Mar 23, 2023
89f7776
feat: 저장된 board를 불러와서 셋팅하는 기능
green-kong Mar 23, 2023
7752a56
feat: 게임 종료시 board의 정보를 저장하는 기능 구현
green-kong Mar 23, 2023
554b60f
fix: 게임 종료시 currentTurn도 함께 저장하도록 변경
green-kong Mar 23, 2023
336578a
feat: 방을 생성하고 저장된 게임을 불러오는 기능 구현
green-kong Mar 23, 2023
0ed8b6e
feat: gameId를 통해 저장된 board를 삭제한다.
green-kong Mar 24, 2023
365c63d
feat: 잘못된 입력을 받은 경우 재입력 받도록 변경
green-kong Mar 24, 2023
9ee341e
refa: TestConnector test 패키지로 이동
green-kong Mar 24, 2023
6564ca8
refa: Controller 역할 분리
green-kong Mar 24, 2023
de234a3
refa: Dao 역할 분리
green-kong Mar 24, 2023
3b6aa4f
refa: service 역할 분리
green-kong Mar 24, 2023
ac91ec5
fix: db table 이름 수정 game -> room
green-kong Mar 24, 2023
e0bd038
chore: add gitignore list
green-kong Mar 24, 2023
18ddbc4
Merge branch 'db' into step3
green-kong Mar 24, 2023
c107c22
fix: dao tableName 수정
green-kong Mar 24, 2023
ca8597c
feat: 기물이 움직일 때 마다 game 상태와 moveHistory 저장하는 기능
green-kong Mar 24, 2023
539b953
fix: 게임이 종료될때 점수가 출력되던 것을 status 명령어를 통해 점수를 출력하도록 변경
green-kong Mar 24, 2023
813988c
refa: 뷰 출력형식 변경
green-kong Mar 24, 2023
da8aae5
fix: 외래키 레퍼런스 테이블 명 변경
green-kong Mar 24, 2023
3de7b5a
feat: gamdId를 통해 최근 moveHistory 2개를 찾는 기능
green-kong Mar 24, 2023
edcd132
feat: moveHistory에서 가장 최근에 인설트된 레코드 두개를 삭제하는 기능 구현
green-kong Mar 24, 2023
9ca0c33
feat: 무르기 기능 구현
green-kong Mar 24, 2023
6fddd5d
feat: 방이름 길이를 제한하는 기능 구현
green-kong Mar 24, 2023
e27035d
refa: 코딩컨벤션
green-kong Mar 24, 2023
ce8c71e
refa: Type enum 이름 변경 -> PieceType
green-kong Mar 26, 2023
ffba7a9
feat: Piece를 생성하는 PieceFactory 기능 구현
green-kong Mar 26, 2023
75aa589
refa: Piece 생성하던 기능
green-kong Mar 26, 2023
bfb724e
refa:ChessBoard와 Score에 분산 되어있던 Score 계산 기능 ScoreCalculator 로 분리
green-kong Mar 26, 2023
33902a6
refa: remove useless print call
green-kong Mar 27, 2023
c3da5ff
refa: 컨트롤러 하나로 합침
green-kong Mar 27, 2023
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ out/

### VS Code ###
.vscode/

### docker ###
/docker/db/mysql/config
/docker/db/mysql/data
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,41 @@
## 우아한테크코스 코드리뷰

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

## ✨ 기능 구현 목록
- [x] 체스보드를 만든다.
- [x] 체스보드는 8 * 8 구성되어있다.
- [x] 세로는 숫자로(1~8), 가로는 알파벳(a~h)


- [x] 게임 시작및 종료 명령어를 입력 받는다.
- [x] start 는 시작
- [x] end 는 종료
- [x] move 는 말의 이동시킨다


- [x] 기물(Piece)를 배치한다.
- [x] 검은색은 대문자로, 흰색은 소문자로 표기한다.


- [x] 말을 이동시킨다.
- [x] 각진영은 자신의 차례에만 말을 움직일 수 있다.
- [x] 비숍은 대각선 방향으로만 움직인다.
- [x] 룩은 직선방향으로만 움직인다.
- [x] 퀸은 직선과 대각선 방향 모두 움직인다.
- [x] 위 세개의 기물은 움직이는 경로에 다른 기물이 잇으면 움직일 수 없다.
- [x] 나이트는 대각선한칸, 직선한칸 움직인다.
- [x] 나이트는 경로에 있는 기물과 상관 없이 움직인다.
- [x] 킹은 상하좌우대각선 한칸씩 움직인다.
- [x] 위의 모든 기물은 도착지점에 같은 진영의말이 있은 경우 해당 지점으로 갈 수 없다.


- [x] 폰을 이동시킨다.
- [x] 폰을 직진 시킨다.
- [x] 첫 움직임인 경우 두칸움직일 수 있다(더블스텝)
- [x] 두칸 움직이는 경우 경로에 다른 말이 있으면 갈수 없다.
- [x] 폰이 직진하는 경우 도착지점은 비어있어야 한다.
- [x] 폰이 대각선으로 한칸 이동한다.
- [x] 폰의 대각선 방향에 상대방의 기물이 있는 경우 상대방의 기물을 잡으며 이동할 수 있다.
- [x] 대각선 방향이 비어있으면 대각선 방향으로 이동할 수없다.
- [x] 폰은 자신의 진영 방향으로 움직일 수 없다. (후진 불가, 전진만 가능)
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repositories {
dependencies {
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'
}

java {
Expand Down
30 changes: 30 additions & 0 deletions docker/db/mysql/init/create_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CREATE TABLE `room`
(
`_id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`gameName` varchar(10),
`currentTurn` varchar(5) default 'WHITE'
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `moveHistory`
(
`_id` int PRIMARY KEY AUTO_INCREMENT,
`source` varchar(2),
`target` varchar(2),
`pieceOnTarget` varchar(6),
`r_id` int
);

CREATE TABLE `board`
(
`_id` int PRIMARY KEY AUTO_INCREMENT,
`square` varchar(2),
`piece` varchar(6),
`camp` varchar(5),
`r_id` int
);

ALTER TABLE `board`
ADD FOREIGN KEY (`r_id`) REFERENCES `room` (`_id`);

ALTER TABLE `moveHistory`
ADD FOREIGN KEY (`r_id`) REFERENCES `room` (`_id`);
19 changes: 19 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: "3.9"
services:
db:
image: mysql:8.0.28
platform: linux/x86_64
restart: always
ports:
- "13306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: chess
MYSQL_USER: user
MYSQL_PASSWORD: password
TZ: Asia/Seoul
volumes:
- ./db/mysql/data:/var/lib/mysql
- ./db/mysql/config:/etc/mysql/conf.d
- ./db/mysql/init:/docker-entrypoint-initdb.d

20 changes: 20 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import controller.Controller;
import repository.connector.ProdConnector;
import repository.game.GameDao;
import repository.game.JdbcGameDao;
import repository.room.JdbcRoomDao;
import repository.room.RoomDao;
import service.ChessService;
import service.GameRoomService;

public class Application {
public static void main(String[] args) {
RoomDao roomDao = new JdbcRoomDao(new ProdConnector());
GameRoomService gameRoomService = new GameRoomService(roomDao);

GameDao gameDao = new JdbcGameDao(new ProdConnector());
ChessService chessService = new ChessService(gameDao);

new Controller(gameRoomService, chessService).run();
}
}
161 changes: 161 additions & 0 deletions src/main/java/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package controller;

import static controller.game.Command.CANCEL;
import static controller.game.Command.COMMAND_INDEX;
import static controller.game.Command.CURRENT_SQUARE_INDEX;
import static controller.game.Command.EMPTY;
import static controller.game.Command.END;
import static controller.game.Command.MOVE;
import static controller.game.Command.MOVE_COMMAND_LENGTH;
import static controller.game.Command.STANDARD_COMMAND_LENGTH;
import static controller.game.Command.START;
import static controller.game.Command.STATUS;
import static controller.game.Command.TARGET_SQUARE_INDEX;
import static controller.room.GameRoomCommand.CREATE;
import static controller.room.GameRoomCommand.EXIT;
import static controller.room.GameRoomCommand.JOIN;
import static controller.room.GameRoomCommand.LIST;
import static controller.room.GameRoomCommand.SPECIAL_ROOM_COMMAND_LENGTH;
import static controller.room.GameRoomCommand.STANDARD_ROOM_COMMAND_LENGTH;
import static controller.room.GameRoomCommand.validateCommandLength;

import java.util.EnumMap;
import java.util.List;

import controller.game.Action;
import controller.game.Command;
import controller.room.GameRoomAction;
import controller.room.GameRoomCommand;
import dto.GameInfoDto;
import dto.ScoreDto;
import service.ChessService;
import service.GameRoomService;
import view.InputView;
import view.OutputView;

public class Controller {
private final GameRoomService gameRoomService;
private final ChessService chessService;
private final EnumMap<GameRoomCommand, GameRoomAction> gameRoomActions = new EnumMap<>(GameRoomCommand.class);
private final EnumMap<Command, Action> actions = new EnumMap<>(Command.class);

public Controller(GameRoomService gameRoomService, ChessService chessService) {
this.gameRoomService = gameRoomService;
this.chessService = chessService;
gameRoomActions.put(CREATE, this::createGameRoom);
gameRoomActions.put(LIST, this::readGameRooms);
gameRoomActions.put(EXIT, this::exitGame);
gameRoomActions.put(JOIN, this::joinGame);
actions.put(START, this::start);
actions.put(MOVE, this::move);
actions.put(END, this::end);
actions.put(STATUS, this::status);
actions.put(CANCEL, this::cancelMove);
}

public void run() {
GameRoomCommand gameRoomCommand = GameRoomCommand.NONE;
while (gameRoomCommand != EXIT) {
gameRoomCommand = runByGameRooAction();
}
}

private GameRoomCommand runByGameRooAction() {
try {
GameRoomCommand gameRoomCommand;
OutputView.printGameRoomInfo();
List<String> inputs = InputView.requestCommand();
gameRoomCommand = GameRoomCommand.find(inputs.get(0));
GameRoomAction gameRoomAction = gameRoomActions.get(gameRoomCommand);
gameRoomAction.execute(inputs);
return gameRoomCommand;
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e);
return GameRoomCommand.NONE;
}
}

private void createGameRoom(List<String> inputs) {
validateCommandLength(inputs.size(), SPECIAL_ROOM_COMMAND_LENGTH);
gameRoomService.createGameRoom(inputs.get(1));
}

private void joinGame(List<String> inputs) {
validateCommandLength(inputs.size(), SPECIAL_ROOM_COMMAND_LENGTH);
String gameName = inputs.get(1);
long roomId = gameRoomService.findRoomIdByRoomName(gameName);
gameStart(roomId);
}

private void readGameRooms(List<String> inputs) {
validateCommandLength(inputs.size(), STANDARD_ROOM_COMMAND_LENGTH);
List<String> gameRooms = gameRoomService.readGameRooms();
OutputView.printGameRooms(gameRooms);
}

private void exitGame(List<String> inputs) {
validateCommandLength(inputs.size(), STANDARD_ROOM_COMMAND_LENGTH);
}

public void gameStart(long roomId) {
GameInfoDto gameInfo = chessService.getGameInfo(roomId);
OutputView.printIsSavedGame(gameInfo.getBoardDtos().size());
chessService.setUp(gameInfo);
OutputView.printChessBoard(chessService.getChessBoard());
OutputView.printChessInfo();
Command command = START;
while (command != END) {
command = play(roomId);
}
chessService.end(roomId);
}

private Command play(long roomId) {
try {
List<String> inputs = InputView.requestCommand();
Command command = Command.find(inputs.get(COMMAND_INDEX));
Action action = actions.get(command);
action.execute(roomId, inputs);
return refreshCommandByResult(command);
} catch (IllegalArgumentException | IllegalStateException e) {
OutputView.printErrorMessage(e);
return EMPTY;
}
}

private Command refreshCommandByResult(Command command) {
if (chessService.isFinished()) {
return END;
}
return command;
}

private void start(long ignored, List<String> inputs) {
Command.validateCommandLength(inputs.size(), STANDARD_COMMAND_LENGTH);
OutputView.printChessBoard(chessService.getChessBoard());
}

private void move(long roomId, List<String> inputs) {
Command.validateCommandLength(inputs.size(), MOVE_COMMAND_LENGTH);
String currentSquareInput = inputs.get(CURRENT_SQUARE_INDEX);
String targetSquareInput = inputs.get(TARGET_SQUARE_INDEX);
chessService.move(roomId, currentSquareInput, targetSquareInput);
OutputView.printChessBoard(chessService.getChessBoard());
}

private void status(long ignored, List<String> inputs) {
Command.validateCommandLength(inputs.size(), STANDARD_COMMAND_LENGTH);
List<ScoreDto> scoreDto = chessService.calculateFinalScore();
OutputView.printScores(scoreDto);
}

private void end(long ignored, List<String> inputs) {
Command.validateCommandLength(inputs.size(), STANDARD_COMMAND_LENGTH);
}

private void cancelMove(long gameId, List<String> inputs) {
Command.validateCommandLength(inputs.size(), STANDARD_COMMAND_LENGTH);
chessService.cancelMove(gameId);
OutputView.printChessBoard(chessService.getChessBoard());
}
}
8 changes: 8 additions & 0 deletions src/main/java/controller/game/Action.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package controller.game;

import java.util.List;

@FunctionalInterface
public interface Action {
void execute(long gameId, List<String> inputs);
}
40 changes: 40 additions & 0 deletions src/main/java/controller/game/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package controller.game;

import java.util.Arrays;

public enum Command {
START("start"),
END("end"),
MOVE("move"),
STATUS("status"),
CANCEL("cancel"),
EMPTY("");

public static final int COMMAND_INDEX = 0;
public static final int CURRENT_SQUARE_INDEX = 1;
public static final int TARGET_SQUARE_INDEX = 2;
public static final int STANDARD_COMMAND_LENGTH = 1;
public static final int MOVE_COMMAND_LENGTH = 3;
public static final String COMMAND_ERROR_MESSAGE = "입력이 올바르지 않습니다.";


private final String value;

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


public static Command find(String command) {
return Arrays.stream(Command.values())
.filter(runCommand -> runCommand.value.equals(command))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("잘못된 명령입니다."));
}

public static void validateCommandLength(int given, int expected) {
if (given != expected) {
throw new IllegalArgumentException(COMMAND_ERROR_MESSAGE);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/controller/room/GameRoomAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package controller.room;

import java.util.List;

@FunctionalInterface
public interface GameRoomAction {
void execute(List<String> inputs);
}
29 changes: 29 additions & 0 deletions src/main/java/controller/room/GameRoomCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package controller.room;

import java.util.Arrays;

public enum GameRoomCommand {
LIST,
CREATE,
EXIT,
JOIN,
NONE;

public static final int STANDARD_ROOM_COMMAND_LENGTH = 1;
public static final int SPECIAL_ROOM_COMMAND_LENGTH = 2;
public static final String ROOM_COMMAND_ERROR_MESSAGE = "입력이 올바르지 않습니다.";

public static GameRoomCommand find(String command) {
return Arrays.stream(GameRoomCommand.values())
.filter(gameRoomCommand -> !gameRoomCommand.equals(NONE))

Choose a reason for hiding this comment

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

요건 어떤 상황 때문에 filter하신 것인지 궁금합니다!

.filter(gameRoomCommand -> gameRoomCommand.name().equals(command))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("올바른 입력이 아닙니다."));
}

public static void validateCommandLength(int given, int expected) {
if (given != expected) {
throw new IllegalArgumentException(ROOM_COMMAND_ERROR_MESSAGE);
}
}
}
Loading