diff --git a/build.gradle b/build.gradle index 19a6df8e4c..0157941326 100644 --- a/build.gradle +++ b/build.gradle @@ -22,8 +22,11 @@ dependencies { implementation 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1' implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.3.1' + implementation 'io.rest-assured:rest-assured:4.3.2' + implementation 'io.rest-assured:json-path:4.3.2' + compile group: 'com.sun.xml.bind', name: 'jaxb-osgi', version: '3.0.0-M5' + testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.rest-assured:rest-assured:3.3.0' runtimeOnly 'mysql:mysql-connector-java' runtimeOnly 'com.h2database:h2' diff --git a/docs/README.md b/docs/README.md index 4e8ebc4109..49f29e2f77 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,32 +4,54 @@ - Spring Framework를 활용하여 애플리케이션을 구동한다. - Spark를 대체하여 Spring MVC를 활용하여 요청을 받고 응답을 한다. - Spring JDBC를 활용하여 DB 접근하던 기존 로직을 대체한다. +
## step1 프로그래밍 요구사항 - 스프링 애플리케이션으로 체스가 실행 가능 해야한다. - @Controller나 @RestController를 활용하여 요청을 받아야 한다. - Spring JDBC에서 제공하는 JdbcTemplate를 이용하여 Connection을 직접 만들어 주는 로직을 대체한다. - JdbcTemplate는 매번 새로 생성하지 않고 빈 주입을 받아서 사용한다. +
+ +## step2 요구사항 +- 체스 게임을 진행할 수 있는 방을 만들어서 동시에 여러 게임이 가능하도록 하기 +
+ +## step2 프로그래밍 요구사항 +### 체스방 만들기 +- localhost:8080 요청 시 노출되는 페이지에 체스방을 만들 수 있는 버튼이 있다. +- 체스방 만들기 버튼을 누르면 새로운 체스판이 초기화된다. +- 체스방에는 고유 식별값이 부여된다. (이 고유 식별값은 체스방 주소에서 사용됨) +### 체스방 목록 조회하기 +- localhost:8080 요청 시 체스방 목록을 조회할 수 있다. +- 체스방 목록에는 체스방 제목이 표시된다. +### 체스방 참여하기 +- localhost:8080 요청 시 체스방 목록에서 체스방을 클릭하면 체스 게임을 이어서 진행할 수 있다. +
## DB DDL ```sql -- ----------------------------------------------------- --- Table `mydb`.`piece` +-- Table `mychess`.`room` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `mydb`.`piece` ( -`id` BIGINT NOT NULL AUTO_INCREMENT, -`piece_name` VARCHAR(45) NOT NULL, -`piece_position` VARCHAR(45) NOT NULL, -PRIMARY KEY (`id`)) -ENGINE = InnoDB; +CREATE TABLE IF NOT EXISTS `mychess`.`room` ( + `room_id` BIGINT NOT NULL AUTO_INCREMENT, + `room_name` VARCHAR(45) NOT NULL, + `current_turn` VARCHAR(45) NOT NULL, + PRIMARY KEY (`room_id`) + ) CHARSET = utf8, ENGINE = InnoDB; -- ----------------------------------------------------- --- Table `mydb`.`turn` +-- Table `mychess`.`piece` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `mydb`.`turn` ( -`id` BIGINT NOT NULL AUTO_INCREMENT, -`current_turn` VARCHAR(45) NOT NULL, -PRIMARY KEY (`id`)) -ENGINE = InnoDB; +CREATE TABLE IF NOT EXISTS `mychess`.`piece` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `piece_name` VARCHAR(45) NOT NULL, + `piece_position` VARCHAR(45) NOT NULL, + `room_id` BIGINT NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`room_id`) + REFERENCES room(`room_id`) + ) CHARSET = utf8, ENGINE = InnoDB; ``` \ No newline at end of file diff --git a/src/main/java/chess/controller/ChessApiController.java b/src/main/java/chess/controller/ChessApiController.java index afc992a3aa..4d26222b8f 100644 --- a/src/main/java/chess/controller/ChessApiController.java +++ b/src/main/java/chess/controller/ChessApiController.java @@ -1,15 +1,20 @@ package chess.controller; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import chess.dto.ChessBoardDto; import chess.dto.request.MoveRequestDto; +import chess.dto.request.RoomNameRequestDto; import chess.dto.request.TurnChangeRequestDto; import chess.dto.response.MoveResponseDto; +import chess.dto.response.RoomIdResponseDto; import chess.service.ChessService; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; @RestController public class ChessApiController { @@ -19,14 +24,28 @@ public ChessApiController(final ChessService chessService) { this.chessService = chessService; } - @PostMapping(value = "/move", produces = MediaType.APPLICATION_JSON_VALUE) - public MoveResponseDto move(@RequestBody MoveRequestDto moveRequestDto) { - return chessService.move(moveRequestDto); + @PostMapping(value = "/api/room") + public RoomIdResponseDto start(@RequestBody final RoomNameRequestDto roomNameRequestDto) { + return new RoomIdResponseDto(chessService.addRoom(roomNameRequestDto.getRoomName())); + } + + @GetMapping("/api/chess/{roomId}") + public ChessBoardDto chess(@PathVariable final Long roomId) { + Map board = chessService.board(roomId); + String currentTurn = chessService.currentTurn(roomId); + return new ChessBoardDto(board, currentTurn); + } + + @PostMapping(value = "/api/move", produces = MediaType.APPLICATION_JSON_VALUE) + public MoveResponseDto move(@RequestBody final MoveRequestDto moveRequestDto) { + boolean isMovable = chessService.move( + moveRequestDto.getSource(), moveRequestDto.getTarget(), moveRequestDto.getRoomId()); + return new MoveResponseDto(isMovable); } - @PostMapping(value = "/turn", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity turn(@RequestBody TurnChangeRequestDto turnChangeRequestDto) { - chessService.changeTurn(turnChangeRequestDto); + @PostMapping(value = "/api/turn", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity turn(@RequestBody final TurnChangeRequestDto turnChangeRequestDto) { + chessService.changeTurn(turnChangeRequestDto.getNextTurn(), turnChangeRequestDto.getRoomId()); return new ResponseEntity<>(HttpStatus.OK); } } diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java index 1003d8a403..ee1d39ef20 100644 --- a/src/main/java/chess/controller/ChessController.java +++ b/src/main/java/chess/controller/ChessController.java @@ -1,13 +1,16 @@ package chess.controller; -import chess.dto.ChessBoardDto; -import chess.dto.StringChessBoardDto; +import chess.dto.RoomsDto; import chess.dto.response.ScoreResponseDto; import chess.service.ChessService; -import com.fasterxml.jackson.core.JsonProcessingException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; @Controller public class ChessController { @@ -17,34 +20,24 @@ public ChessController(final ChessService chessService) { this.chessService = chessService; } - @GetMapping("/start") - public String start() { - chessService.makeRound(); - return makeNewGame(); - } - - @GetMapping("/reset") - public String reset() { - chessService.resetRound(); - return makeNewGame(); + @GetMapping("/rooms") + public String rooms(final Model model) { + List roomsDto = new ArrayList<>(); + Map rooms = chessService.rooms(); + for (Map.Entry roomEntry : rooms.entrySet()) { + roomsDto.add(new RoomsDto(roomEntry.getKey(), roomEntry.getValue())); + } + model.addAttribute("rooms", roomsDto); + return "rooms"; } - @GetMapping("/chess") - public String chess(final Model model) throws JsonProcessingException { - ChessBoardDto chessBoard = chessService.chessBoardFromDB(); - String jsonFormatChessBoard = chessService.jsonFormatChessBoard(chessBoard); - model.addAttribute("jsonFormatChessBoard", jsonFormatChessBoard); - String currentTurn = chessService.currentTurn(); - model.addAttribute("currentTurn", currentTurn); - chessService.updateRound(chessBoard, currentTurn); - - ScoreResponseDto scoreResponseDto = chessService.scoreResponseDto(); + @GetMapping("/chess/{roomId}") + public String chess(@PathVariable final Long roomId, final Model model) { + double whiteScore = chessService.whiteScore(roomId); + double blackScore = chessService.blackScore(roomId); + ScoreResponseDto scoreResponseDto = new ScoreResponseDto(whiteScore, blackScore); + model.addAttribute("roomId", roomId); model.addAttribute("score", scoreResponseDto); return "chess"; } - - private String makeNewGame() { - chessService.initialize(); - return "redirect:/chess"; - } } diff --git a/src/main/java/chess/dao/PieceDao.java b/src/main/java/chess/dao/PieceDao.java index ba10ac51f0..beaad5efb6 100644 --- a/src/main/java/chess/dao/PieceDao.java +++ b/src/main/java/chess/dao/PieceDao.java @@ -1,75 +1,45 @@ package chess.dao; -import chess.dao.setting.DBConnection; -import chess.dto.request.MoveRequestDto; -import chess.dto.response.ChessResponseDto; -import org.springframework.beans.factory.annotation.Autowired; +import chess.dao.dto.response.ChessResponseDto; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.sql.DataSource; -import java.util.ArrayList; import java.util.List; @Repository -public class PieceDao extends DBConnection { - @Autowired +public class PieceDao { private final JdbcTemplate jdbcTemplate; - public PieceDao(DataSource dataSource) { + public PieceDao(final DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } - public void initializePieceStatus(final String pieceName, final String piecePosition) { - String query = "INSERT INTO piece (piece_name, piece_position) VALUE (?, ?)"; - try { - jdbcTemplate.update(query, pieceName, piecePosition); - } catch (Exception e) { - e.printStackTrace(); - } + public void initializePieceStatus(final String pieceName, final String piecePosition, + final Long roomId) { + String query = "INSERT INTO piece (piece_name, piece_position, room_id) VALUE (?, ?, ?)"; + jdbcTemplate.update(query, pieceName, piecePosition, roomId); } - public List showAllPieces() { - List pieces = new ArrayList<>(); - String query = "SELECT * FROM piece"; - - try { - pieces = jdbcTemplate.query( - query, (rs, rowNum) -> new ChessResponseDto( - rs.getLong("id"), - rs.getString("piece_name"), - rs.getString("piece_position")) - ); - } catch (Exception e) { - e.printStackTrace(); - } - return pieces; + public List findAllPieces(final Long roomId) { + String query = "SELECT * FROM piece WHERE room_id=?"; + return jdbcTemplate.query( + query, + (rs, rowNum) -> new ChessResponseDto( + rs.getLong("id"), + rs.getString("piece_name"), + rs.getString("piece_position")), + roomId + ); } - public void movePiece(final MoveRequestDto moveRequestDto) { + public void movePiece(final String source, final String target) { String query = "UPDATE piece SET piece_position=? WHERE piece_position=?"; - try { - jdbcTemplate.update(query, moveRequestDto.getTarget(), moveRequestDto.getSource()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void removeAllPieces() { - String query = "DELETE FROM piece"; - try { - jdbcTemplate.update(query); - } catch (Exception e) { - e.printStackTrace(); - } + jdbcTemplate.update(query, target, source); } - public void removePiece(final MoveRequestDto moveRequestDto) { + public void removePiece(final String target) { String query = "DELETE FROM piece WHERE piece_position=?"; - try { - jdbcTemplate.update(query, moveRequestDto.getTarget()); - } catch (Exception e) { - e.printStackTrace(); - } + jdbcTemplate.update(query, target); } } diff --git a/src/main/java/chess/dao/RoomDao.java b/src/main/java/chess/dao/RoomDao.java new file mode 100644 index 0000000000..379744ea78 --- /dev/null +++ b/src/main/java/chess/dao/RoomDao.java @@ -0,0 +1,56 @@ +package chess.dao; + +import chess.dao.dto.response.RoomResponseDto; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.util.List; +import java.util.Objects; + +@Repository +public class RoomDao { + private final JdbcTemplate jdbcTemplate; + + public RoomDao(final DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + public Long addRoom(final String roomName) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + String query = "INSERT INTO room (room_name, current_turn) VALUES (?, ?)"; + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(query, new String[]{"room_id"}); + ps.setString(1, roomName); + ps.setString(2, "white"); + return ps; + }, keyHolder); + return Objects.requireNonNull(keyHolder.getKey()).longValue(); + } + + public List findAllRooms() { + List rooms; + String query = "SELECT * FROM room"; + rooms = jdbcTemplate.query( + query, + (rs, rowName) -> new RoomResponseDto( + rs.getLong("room_id"), + rs.getString("room_name"), + rs.getString("current_turn")) + ); + return rooms; + } + + public String findCurrentTurn(final Long roomId) { + String query = "SELECT current_turn FROM room WHERE room_id=?"; + return jdbcTemplate.queryForObject(query, String.class, roomId); + } + + public void changeTurn(final String nextTurn, final Long roomId) { + String query = "UPDATE room SET current_turn=? WHERE room_id=?"; + jdbcTemplate.update(query, nextTurn, roomId); + } +} diff --git a/src/main/java/chess/dao/TurnDao.java b/src/main/java/chess/dao/TurnDao.java deleted file mode 100644 index ec8e35f694..0000000000 --- a/src/main/java/chess/dao/TurnDao.java +++ /dev/null @@ -1,66 +0,0 @@ -package chess.dao; - -import chess.dao.setting.DBConnection; -import chess.dto.request.TurnChangeRequestDto; -import chess.dto.response.TurnResponseDto; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; - -import javax.sql.DataSource; -import java.util.ArrayList; -import java.util.List; - -@Repository -public class TurnDao extends DBConnection { - @Autowired - private final JdbcTemplate jdbcTemplate; - - public TurnDao(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } - - public void initializeTurn() { - String query = "INSERT INTO turn (current_turn) VALUE (?)"; - try { - jdbcTemplate.update(query, "white"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public List showCurrentTurn() { - List turn = new ArrayList<>(); - String query = "SELECT * FROM turn"; - - try { - turn = jdbcTemplate.query( - query, (rs, rowNum) -> new TurnResponseDto( - rs.getLong("id"), - rs.getString("current_turn")) - ); - } catch (Exception e) { - e.printStackTrace(); - } - return turn; - } - - public void changeTurn(final TurnChangeRequestDto turnChangeRequestDto) { - String query = "UPDATE turn SET current_turn=? WHERE current_turn=?"; - try { - jdbcTemplate.update(query, - turnChangeRequestDto.getNextTurn(), turnChangeRequestDto.getCurrentTurn()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void removeTurn() { - String query = "DELETE FROM turn"; - try { - jdbcTemplate.update(query); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/chess/dto/response/ChessResponseDto.java b/src/main/java/chess/dao/dto/response/ChessResponseDto.java similarity index 94% rename from src/main/java/chess/dto/response/ChessResponseDto.java rename to src/main/java/chess/dao/dto/response/ChessResponseDto.java index 6c2eb598fa..e6bc05478a 100644 --- a/src/main/java/chess/dto/response/ChessResponseDto.java +++ b/src/main/java/chess/dao/dto/response/ChessResponseDto.java @@ -1,4 +1,4 @@ -package chess.dto.response; +package chess.dao.dto.response; public class ChessResponseDto { private final Long id; diff --git a/src/main/java/chess/dao/dto/response/RoomResponseDto.java b/src/main/java/chess/dao/dto/response/RoomResponseDto.java new file mode 100644 index 0000000000..ffb352e32b --- /dev/null +++ b/src/main/java/chess/dao/dto/response/RoomResponseDto.java @@ -0,0 +1,25 @@ +package chess.dao.dto.response; + +public class RoomResponseDto { + private final long roomId; + private final String roomName; + private final String currentTurn; + + public RoomResponseDto(final long roomId, final String roomName, final String currentTurn) { + this.roomId = roomId; + this.roomName = roomName; + this.currentTurn = currentTurn; + } + + public long getRoomId() { + return roomId; + } + + public String getRoomName() { + return roomName; + } + + public String getCurrentTurn() { + return currentTurn; + } +} diff --git a/src/main/java/chess/dao/setting/DBConnection.java b/src/main/java/chess/dao/setting/DBConnection.java deleted file mode 100644 index ccdfbcb5d8..0000000000 --- a/src/main/java/chess/dao/setting/DBConnection.java +++ /dev/null @@ -1,46 +0,0 @@ -package chess.dao.setting; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -public class DBConnection { - public Connection getConnection() { - final String server = "localhost:13306"; // MySQL 서버 주소 - final String database = "mydb"; // MySQL DATABASE 이름 - final String option = "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC"; - final String userName = "da-nyee"; // MySQL 서버 아이디 - final String password = "1234"; // MySQL 서버 비밀번호 - - Connection con = null; - - // 드라이버 로딩 - try { - Class.forName("com.mysql.cj.jdbc.Driver"); - } catch (ClassNotFoundException e) { - System.err.println(" !! JDBC Driver load 오류: " + e.getMessage()); - e.printStackTrace(); - } - - // 드라이버 연결 - try { - con = DriverManager.getConnection("jdbc:mysql://" + server + "/" + database + option, userName, password); - System.out.println("정상적으로 연결되었습니다."); - } catch (SQLException e) { - System.err.println("연결 오류:" + e.getMessage()); - e.printStackTrace(); - } - - return con; - } - - // 드라이버 연결해제 - public void closeConnection(Connection con) { - try { - if (con != null) - con.close(); - } catch (SQLException e) { - System.err.println("con 오류:" + e.getMessage()); - } - } -} diff --git a/src/main/java/chess/domain/board/ChessBoardFactory.java b/src/main/java/chess/domain/board/ChessBoardFactory.java index 8ff40f7fdb..77226bdcf9 100644 --- a/src/main/java/chess/domain/board/ChessBoardFactory.java +++ b/src/main/java/chess/domain/board/ChessBoardFactory.java @@ -18,12 +18,12 @@ public static Map initializeBoard() { return board; } - public static Map loadBoard(final Map boardFromDB) { + public static Map loadBoard(final Map pieces) { Map board = new LinkedHashMap<>(); - for (Map.Entry boardFromDBEntry : boardFromDB.entrySet()) { + for (Map.Entry piecesEntry : pieces.entrySet()) { board.put(Position.findByString( - boardFromDBEntry.getKey()), - PieceFactory.createByString(boardFromDBEntry.getValue(), boardFromDBEntry.getKey())); + piecesEntry.getKey()), + PieceFactory.createByString(piecesEntry.getValue(), piecesEntry.getKey())); } return board; } diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java index e49dcd0dc8..a186279dae 100644 --- a/src/main/java/chess/domain/piece/Piece.java +++ b/src/main/java/chess/domain/piece/Piece.java @@ -48,6 +48,10 @@ public final boolean isSamePosition(final Position position) { return this.position.equals(position); } + public final boolean isSameColor(final String name) { + return this.color.name().equals(name.toUpperCase()); + } + public final void changePosition(final Position position) { this.position = position; } diff --git a/src/main/java/chess/domain/piece/PiecesFactory.java b/src/main/java/chess/domain/piece/PiecesFactory.java index 9f46e87357..d0160760e9 100644 --- a/src/main/java/chess/domain/piece/PiecesFactory.java +++ b/src/main/java/chess/domain/piece/PiecesFactory.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; public class PiecesFactory { private static final String KING = "k"; @@ -28,7 +29,6 @@ public static Pieces whitePieces() { for (int idx = 0; idx < PAWN_SIZE; idx++) { pieces.add(Pawn.from(PAWN, Position.valueOf("2", Character.toString((char) ('a' + idx))))); } - return new Pieces(pieces); } @@ -46,7 +46,21 @@ public static Pieces blackPieces() { for (int idx = 0; idx < PAWN_SIZE; idx++) { pieces.add(Pawn.from(PAWN.toUpperCase(), Position.valueOf("7", Character.toString((char) ('a' + idx))))); } + return new Pieces(pieces); + } + public static Pieces pieces(final Map board, final String name) { + List pieces = new ArrayList<>(); + for (Map.Entry boardEntry : board.entrySet()) { + Piece piece = boardEntry.getValue(); + isSameColor(pieces, piece, name); + } return new Pieces(pieces); } + + private static void isSameColor(final List pieces, final Piece piece, final String name) { + if (piece.isSameColor(name)) { + pieces.add(piece); + } + } } diff --git a/src/main/java/chess/domain/player/BlackPlayer.java b/src/main/java/chess/domain/player/BlackPlayer.java index c99937b33d..e5f80deae9 100644 --- a/src/main/java/chess/domain/player/BlackPlayer.java +++ b/src/main/java/chess/domain/player/BlackPlayer.java @@ -1,8 +1,18 @@ package chess.domain.player; +import chess.domain.piece.Piece; +import chess.domain.position.Position; import chess.domain.state.State; +import java.util.Map; + public class BlackPlayer extends Player { + private static final String NAME = "black"; + + public BlackPlayer(final Map board, final String currentTurn) { + super(board, currentTurn, NAME); + } + public BlackPlayer(final State state) { super(state); } diff --git a/src/main/java/chess/domain/player/Player.java b/src/main/java/chess/domain/player/Player.java index a840746334..57a5a3a221 100644 --- a/src/main/java/chess/domain/player/Player.java +++ b/src/main/java/chess/domain/player/Player.java @@ -1,13 +1,29 @@ package chess.domain.player; +import chess.domain.piece.Piece; import chess.domain.piece.Pieces; +import chess.domain.piece.PiecesFactory; +import chess.domain.position.Position; import chess.domain.position.Source; import chess.domain.position.Target; +import chess.domain.state.FinishedTurn; +import chess.domain.state.RunningTurn; import chess.domain.state.State; +import java.util.Map; + public abstract class Player { private State state; + public Player(final Map board, final String currentTurn, final String name) { + if (name.equals(currentTurn)) { + this.state = new RunningTurn(PiecesFactory.pieces(board, name)); + } + if (!name.equals(currentTurn)) { + this.state = new FinishedTurn(PiecesFactory.pieces(board, name)); + } + } + protected Player(final State state) { this.state = state; } @@ -28,10 +44,6 @@ public final void toRunningState(final State anotherState) { this.state = this.state.toRunningTurn(anotherState); } - public final void changeState(final State nextState) { - this.state = nextState; - } - public final State getState() { return state; } diff --git a/src/main/java/chess/domain/player/Round.java b/src/main/java/chess/domain/player/Round.java index da2fbc76db..8803bf3e10 100644 --- a/src/main/java/chess/domain/player/Round.java +++ b/src/main/java/chess/domain/player/Round.java @@ -2,6 +2,7 @@ import chess.domain.board.ChessBoardFactory; import chess.domain.command.Command; +import chess.domain.command.CommandFactory; import chess.domain.piece.Piece; import chess.domain.piece.Pieces; import chess.domain.position.Position; @@ -18,10 +19,19 @@ public class Round { private Map board; private Command command; - private boolean isEnd = false; + private String currentTurn; + + public Round(final Map board, final String currentTurn, final String command) { + this.whitePlayer = new WhitePlayer(board, currentTurn); + this.blackPlayer = new BlackPlayer(board, currentTurn); + this.board = board; + this.command = CommandFactory.initialCommand(command); + this.currentTurn = currentTurn; + } public Round(final State white, final State black, final Command command) { this(new WhitePlayer(white), new BlackPlayer(black), command); + this.currentTurn = "white"; } public Round(final Player whitePlayer, final Player blackPlayer, final Command command) { @@ -30,6 +40,32 @@ public Round(final Player whitePlayer, final Player blackPlayer, final Command c this.command = command; } + public static Round of(final Map board, final String currentTurn) { + int totalKing = 0; + for (Map.Entry boardEntry : board.entrySet()) { + totalKing = countKing(totalKing, boardEntry); + } + if (isAllKingAlive(totalKing)) { + return new Round(board, currentTurn, "start"); + } + return new Round(board, currentTurn, "end"); + } + + private static int countKing(int totalKing, final Map.Entry boardEntry) { + if (isKing(boardEntry)) { + totalKing++; + } + return totalKing; + } + + private static boolean isKing(Map.Entry boardEntry) { + return "k".equals(boardEntry.getValue().getPiece()) || "K".equals(boardEntry.getValue().getPiece()); + } + + private static boolean isAllKingAlive(int totalKing) { + return totalKing == 2; + } + public void execute(final Queue commands) { this.command = this.command.execute(commands.poll()); if (this.command.isMove()) { @@ -59,36 +95,11 @@ private void move(final Player currentPlayer, final Player anotherPlayer, private void checkPieces(final State state, final Target target) { if (state.isKing(target.getPosition())) { - isEnd = true; + this.command = command.end(); } if (state.findPiece(target.getPosition()).isPresent()) { state.removePiece(target.getPosition()); } - if (isEnd) { - changeToEnd(); - } - } - - public void changeTurn(final String currentTurn) { - if ("white".equals(currentTurn)) { - State nextWhiteTurn = whitePlayer.getState().toRunningTurn(); - State nextBlackTurn = blackPlayer.getState().toFinishedTurn(); - changeState(nextWhiteTurn, nextBlackTurn); - } - if ("black".equals(currentTurn)) { - State nextWhiteTurn = whitePlayer.getState().toFinishedTurn(); - State nextBlackTurn = blackPlayer.getState().toRunningTurn(); - changeState(nextWhiteTurn, nextBlackTurn); - } - } - - private void changeState(final State nextWhiteTurn, final State nextBlackTurn) { - whitePlayer.changeState(nextWhiteTurn); - blackPlayer.changeState(nextBlackTurn); - } - - public void changeToEnd() { - this.command = command.end(); } public Map getBoard() { diff --git a/src/main/java/chess/domain/player/WhitePlayer.java b/src/main/java/chess/domain/player/WhitePlayer.java index 6bf0bbae25..6a0af5fa07 100644 --- a/src/main/java/chess/domain/player/WhitePlayer.java +++ b/src/main/java/chess/domain/player/WhitePlayer.java @@ -1,8 +1,18 @@ package chess.domain.player; +import chess.domain.piece.Piece; +import chess.domain.position.Position; import chess.domain.state.State; +import java.util.Map; + public class WhitePlayer extends Player { + private static final String NAME = "white"; + + public WhitePlayer(final Map board, final String currentTurn) { + super(board, currentTurn, NAME); + } + public WhitePlayer(final State state) { super(state); } diff --git a/src/main/java/chess/dto/ChessBoardDto.java b/src/main/java/chess/dto/ChessBoardDto.java index 13eb131deb..89dae4ab7e 100644 --- a/src/main/java/chess/dto/ChessBoardDto.java +++ b/src/main/java/chess/dto/ChessBoardDto.java @@ -7,12 +7,18 @@ public class ChessBoardDto { private final Map chessBoard; + private final String currentTurn; - public ChessBoardDto(final Map chessBoard) { + public ChessBoardDto(final Map chessBoard, final String currentTurn) { this.chessBoard = chessBoard; + this.currentTurn = currentTurn; } public Map getChessBoard() { return chessBoard; } + + public String getCurrentTurn() { + return currentTurn; + } } diff --git a/src/main/java/chess/dto/PiecesDto.java b/src/main/java/chess/dto/PiecesDto.java deleted file mode 100644 index a51a27cc3f..0000000000 --- a/src/main/java/chess/dto/PiecesDto.java +++ /dev/null @@ -1,23 +0,0 @@ -package chess.dto; - -import chess.domain.piece.Piece; - -import java.util.List; - -public class PiecesDto { - private final List whitePieces; - private final List blackPieces; - - public PiecesDto(final List whitePieces, final List blackPieces) { - this.whitePieces = whitePieces; - this.blackPieces = blackPieces; - } - - public List getWhitePieces() { - return whitePieces; - } - - public List getBlackPieces() { - return blackPieces; - } -} diff --git a/src/main/java/chess/dto/PlayerDto.java b/src/main/java/chess/dto/PlayerDto.java deleted file mode 100644 index 9b74c59134..0000000000 --- a/src/main/java/chess/dto/PlayerDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package chess.dto; - -import chess.domain.player.Player; - -public class PlayerDto { - private final Player whitePlayer; - private final Player blackPlayer; - - public PlayerDto(final Player whitePlayer, final Player blackPlayer) { - this.whitePlayer = whitePlayer; - this.blackPlayer = blackPlayer; - } - - public Player getWhitePlayer() { - return whitePlayer; - } - - public Player getBlackPlayer() { - return blackPlayer; - } -} diff --git a/src/main/java/chess/dto/RoomsDto.java b/src/main/java/chess/dto/RoomsDto.java new file mode 100644 index 0000000000..587fae6cea --- /dev/null +++ b/src/main/java/chess/dto/RoomsDto.java @@ -0,0 +1,19 @@ +package chess.dto; + +public class RoomsDto { + private final Long roomId; + private final String roomName; + + public RoomsDto(final Long roomId, final String roomName) { + this.roomId = roomId; + this.roomName = roomName; + } + + public Long getRoomId() { + return roomId; + } + + public String getRoomName() { + return roomName; + } +} diff --git a/src/main/java/chess/dto/StringChessBoardDto.java b/src/main/java/chess/dto/StringChessBoardDto.java deleted file mode 100644 index c5dd6a9b39..0000000000 --- a/src/main/java/chess/dto/StringChessBoardDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package chess.dto; - -import java.util.Map; - -public class StringChessBoardDto { - private final Map stringChessBoard; - - public StringChessBoardDto(final Map stringChessBoard) { - this.stringChessBoard = stringChessBoard; - } - - public Map getStringChessBoard() { - return stringChessBoard; - } -} diff --git a/src/main/java/chess/dto/request/MoveRequestDto.java b/src/main/java/chess/dto/request/MoveRequestDto.java index 8cfd68a281..83a922d369 100644 --- a/src/main/java/chess/dto/request/MoveRequestDto.java +++ b/src/main/java/chess/dto/request/MoveRequestDto.java @@ -1,12 +1,17 @@ package chess.dto.request; +import java.beans.ConstructorProperties; + public class MoveRequestDto { private final String source; private final String target; + private final Long roomId; - public MoveRequestDto(final String source, final String target) { + @ConstructorProperties({"source", "target", "roomId"}) + public MoveRequestDto(final String source, final String target, final Long roomId) { this.source = source; this.target = target; + this.roomId = roomId; } public String getSource() { @@ -16,4 +21,8 @@ public String getSource() { public String getTarget() { return target; } + + public Long getRoomId() { + return roomId; + } } diff --git a/src/main/java/chess/dto/request/RoomNameRequestDto.java b/src/main/java/chess/dto/request/RoomNameRequestDto.java new file mode 100644 index 0000000000..a7fa21116c --- /dev/null +++ b/src/main/java/chess/dto/request/RoomNameRequestDto.java @@ -0,0 +1,16 @@ +package chess.dto.request; + +import java.beans.ConstructorProperties; + +public class RoomNameRequestDto { + private final String roomName; + + @ConstructorProperties({"roomName"}) + public RoomNameRequestDto(final String roomName) { + this.roomName = roomName; + } + + public String getRoomName() { + return roomName; + } +} diff --git a/src/main/java/chess/dto/request/TurnChangeRequestDto.java b/src/main/java/chess/dto/request/TurnChangeRequestDto.java index 612d7b54f7..d94a7bb42d 100644 --- a/src/main/java/chess/dto/request/TurnChangeRequestDto.java +++ b/src/main/java/chess/dto/request/TurnChangeRequestDto.java @@ -1,12 +1,17 @@ package chess.dto.request; +import java.beans.ConstructorProperties; + public class TurnChangeRequestDto { private final String currentTurn; private final String nextTurn; + private final Long roomId; - public TurnChangeRequestDto(final String currentTurn, final String nextTurn) { + @ConstructorProperties({"currentTurn, nextTurn, roomId"}) + public TurnChangeRequestDto(final String currentTurn, final String nextTurn, final Long roomId) { this.currentTurn = currentTurn; this.nextTurn = nextTurn; + this.roomId = roomId; } public String getCurrentTurn() { @@ -16,4 +21,8 @@ public String getCurrentTurn() { public String getNextTurn() { return nextTurn; } + + public Long getRoomId() { + return roomId; + } } diff --git a/src/main/java/chess/dto/response/RoomIdResponseDto.java b/src/main/java/chess/dto/response/RoomIdResponseDto.java new file mode 100644 index 0000000000..46ff17e535 --- /dev/null +++ b/src/main/java/chess/dto/response/RoomIdResponseDto.java @@ -0,0 +1,13 @@ +package chess.dto.response; + +public class RoomIdResponseDto { + private final Long roomId; + + public RoomIdResponseDto(final Long roomId) { + this.roomId = roomId; + } + + public Long getRoomId() { + return roomId; + } +} diff --git a/src/main/java/chess/dto/response/TurnResponseDto.java b/src/main/java/chess/dto/response/TurnResponseDto.java deleted file mode 100644 index 3e80571605..0000000000 --- a/src/main/java/chess/dto/response/TurnResponseDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package chess.dto.response; - -public class TurnResponseDto { - private final Long id; - private final String currentTurn; - - public TurnResponseDto(final Long id, final String currentTurn) { - this.id = id; - this.currentTurn = currentTurn; - } - - public String getCurrentTurn() { - return currentTurn; - } -} diff --git a/src/main/java/chess/repository/ChessRepository.java b/src/main/java/chess/repository/ChessRepository.java index 7a6ef344ca..b6654a7c2a 100644 --- a/src/main/java/chess/repository/ChessRepository.java +++ b/src/main/java/chess/repository/ChessRepository.java @@ -1,61 +1,66 @@ package chess.repository; import chess.dao.PieceDao; -import chess.dao.TurnDao; -import chess.dto.request.MoveRequestDto; -import chess.dto.request.TurnChangeRequestDto; -import chess.dto.response.ChessResponseDto; -import chess.dto.response.TurnResponseDto; +import chess.dao.RoomDao; +import chess.dao.dto.response.ChessResponseDto; +import chess.dao.dto.response.RoomResponseDto; import org.springframework.stereotype.Repository; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @Repository public class ChessRepository { + private final RoomDao roomDao; private final PieceDao pieceDao; - private final TurnDao turnDao; - public ChessRepository(PieceDao pieceDao, TurnDao turnDao) { + public ChessRepository(final RoomDao roomDao, final PieceDao pieceDao) { + this.roomDao = roomDao; this.pieceDao = pieceDao; - this.turnDao = turnDao; } - public void initializePieceStatus(final Map board) { - for (Map.Entry boardStatus : board.entrySet()) { - pieceDao.initializePieceStatus(boardStatus.getValue(), boardStatus.getKey()); - } - } - - public void initializeTurn() { - turnDao.initializeTurn(); + public Long addRoom(final String roomName) { + return roomDao.addRoom(roomName); } - public List showAllPieces() { - return pieceDao.showAllPieces(); + public void initializePieceStatus(final Map board, final Long roomId) { + for (Map.Entry boardStatus : board.entrySet()) { + pieceDao.initializePieceStatus(boardStatus.getValue(), boardStatus.getKey(), roomId); + } } - public List showCurrentTurn() { - return turnDao.showCurrentTurn(); + public Map findAllRooms() { + Map rooms = new LinkedHashMap<>(); + List roomResponsesDto = roomDao.findAllRooms(); + for (RoomResponseDto roomResponseDto : roomResponsesDto) { + rooms.put(roomResponseDto.getRoomId(), roomResponseDto.getRoomName()); + } + return rooms; } - public void movePiece(final MoveRequestDto moveRequestDto) { - pieceDao.movePiece(moveRequestDto); + public Map findAllPieces(final Long roomId) { + Map pieces = new LinkedHashMap<>(); + List ChessResponsesDto = pieceDao.findAllPieces(roomId); + for (ChessResponseDto chessResponseDto : ChessResponsesDto) { + pieces.put(chessResponseDto.getPiecePosition(), chessResponseDto.getPieceName()); + } + return pieces; } - public void changeTurn(final TurnChangeRequestDto turnChangeRequestDto) { - turnDao.changeTurn(turnChangeRequestDto); + public String findCurrentTurn(final Long roomId) { + return roomDao.findCurrentTurn(roomId); } - public void removeAllPieces() { - pieceDao.removeAllPieces(); + public void movePiece(final String source, final String target) { + pieceDao.movePiece(source, target); } - public void removeTurn() { - turnDao.removeTurn(); + public void changeTurn(final String nextTurn, final Long roomId) { + roomDao.changeTurn(nextTurn, roomId); } - public void removePiece(final MoveRequestDto moveRequestDto) { - pieceDao.removePiece(moveRequestDto); + public void removePiece(final String target) { + pieceDao.removePiece(target); } } diff --git a/src/main/java/chess/service/ChessService.java b/src/main/java/chess/service/ChessService.java index 0ab742acf0..0a7a1a243d 100644 --- a/src/main/java/chess/service/ChessService.java +++ b/src/main/java/chess/service/ChessService.java @@ -3,182 +3,97 @@ import chess.domain.board.ChessBoardFactory; import chess.domain.command.CommandFactory; import chess.domain.piece.Piece; -import chess.domain.piece.Pieces; import chess.domain.piece.PiecesFactory; -import chess.domain.player.Player; import chess.domain.player.Round; import chess.domain.position.Position; import chess.domain.state.StateFactory; -import chess.dto.ChessBoardDto; -import chess.dto.PiecesDto; -import chess.dto.PlayerDto; -import chess.dto.StringChessBoardDto; -import chess.dto.request.MoveRequestDto; -import chess.dto.request.TurnChangeRequestDto; -import chess.dto.response.ChessResponseDto; -import chess.dto.response.MoveResponseDto; -import chess.dto.response.ScoreResponseDto; -import chess.dto.response.TurnResponseDto; import chess.repository.ChessRepository; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Service; import java.util.*; -import java.util.stream.Collectors; @Service public class ChessService { private final ChessRepository chessRepository; - private Round round; public ChessService(final ChessRepository chessRepository) { this.chessRepository = chessRepository; - this.round = makeRound(); } - public void resetRound() { - round = makeRound(); - } - - public Round makeRound() { - remove(); - return new Round(StateFactory.initialization(PiecesFactory.whitePieces()), + public Long addRoom(final String roomName) { + Round round = new Round(StateFactory.initialization(PiecesFactory.whitePieces()), StateFactory.initialization(PiecesFactory.blackPieces()), CommandFactory.initialCommand("start")); + Long roomId = chessRepository.addRoom(roomName); + initialize(round, roomId); + return roomId; } - private void remove() { - chessRepository.removeAllPieces(); - chessRepository.removeTurn(); - } - - public ChessBoardDto chessBoardFromDB() { - StringChessBoardDto dbChessBoard = dbChessBoard(); - return new ChessBoardDto(round.getBoard( - ChessBoardFactory.loadBoard(dbChessBoard.getStringChessBoard()))); - } - - private StringChessBoardDto dbChessBoard() { - Map dbChessBoard = new LinkedHashMap<>(); - List pieces = chessRepository.showAllPieces(); - for (ChessResponseDto piece : pieces) { - dbChessBoard.put(piece.getPiecePosition(), piece.getPieceName()); - } - chessRepository.removeAllPieces(); - return new StringChessBoardDto(dbChessBoard); - } - - public ChessBoardDto chessBoard() { - return new ChessBoardDto(round.getBoard()); - } - - public StringChessBoardDto stringChessBoard(final ChessBoardDto chessBoard) { - Map stringChessBoard = new LinkedHashMap<>(); - for (Map.Entry chessBoardEntry : chessBoard.getChessBoard().entrySet()) { - stringChessBoard.put(chessBoardEntry.getKey().toString(), chessBoardEntry.getValue().getPiece()); - } - chessRepository.initializePieceStatus(stringChessBoard); - return new StringChessBoardDto(stringChessBoard); - } - - public PiecesDto piecesDto(final ChessBoardDto chessBoard) { - List whitePieces = new ArrayList<>(); - List blackPieces = new ArrayList<>(); - for (Map.Entry chessBoardEntry : chessBoard.getChessBoard().entrySet()) { - filterPieces(whitePieces, blackPieces, chessBoardEntry); + private void initialize(final Round round, final Long roomId) { + Map filteredBoard = new LinkedHashMap<>(); + Map board = round.getBoard(); + for (Map.Entry boardEntry : board.entrySet()) { + filterBoard(filteredBoard, boardEntry); } - return new PiecesDto(whitePieces, blackPieces); + chessRepository.initializePieceStatus(filteredBoard, roomId); } - private void filterPieces(final List whitePieces, final List blackPieces, - final Map.Entry chessBoardEntry) { - if (chessBoardEntry.getValue().isBlack()) { - blackPieces.add(chessBoardEntry.getValue()); - return; + private void filterBoard(final Map filteredBoard, + final Map.Entry boardEntry) { + if (boardEntry.getValue() != null) { + filteredBoard.put(boardEntry.getKey().toString(), boardEntry.getValue().getPiece()); } - whitePieces.add(chessBoardEntry.getValue()); - } - - public String jsonFormatChessBoard(final ChessBoardDto chessBoard) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - StringChessBoardDto stringChessBoard = stringChessBoard(chessBoard); - return objectMapper.writeValueAsString(stringChessBoard.getStringChessBoard()); } - public String currentTurn() { - List turns = chessRepository.showCurrentTurn(); - return turns.stream() - .map(TurnResponseDto::getCurrentTurn) - .collect(Collectors.joining()); + public Map board(final Long roomId) { + Round round = round(roomId); + Map pieces = pieces(roomId); + return round.getBoard(ChessBoardFactory.loadBoard(pieces)); } - public void updateRound(final ChessBoardDto chessBoard, final String currentTurn) { - PiecesDto piecesDto = piecesDto(chessBoard); - round = new Round(StateFactory.initialization(new Pieces(piecesDto.getWhitePieces())), - StateFactory.initialization(new Pieces(piecesDto.getBlackPieces())), - CommandFactory.initialCommand("start")); - round.changeTurn(currentTurn); + public Map rooms() { + return chessRepository.findAllRooms(); } - public PlayerDto playerDto() { - Player whitePlayer = round.getWhitePlayer(); - Player blackPlayer = round.getBlackPlayer(); - return new PlayerDto(whitePlayer, blackPlayer); - } - - public ScoreResponseDto scoreResponseDto() { - PlayerDto playerDto = playerDto(); - double whiteScore = playerDto.getWhitePlayer().calculateScore(); - double blackScore = playerDto.getBlackPlayer().calculateScore(); - changeRoundToEnd(); - return new ScoreResponseDto(whiteScore, blackScore); + public boolean move(final String source, final String target, final Long roomId) { + Queue commands = + new ArrayDeque<>(Arrays.asList("move", source, target)); + Round round = round(roomId); + round.execute(commands); + movePiece(source, target); + return true; } - private void changeRoundToEnd() { - PlayerDto playerDto = playerDto(); - if (!(playerDto.getWhitePlayer().getPieces().isKing() && - playerDto.getBlackPlayer().getPieces().isKing())) { - round.changeToEnd(); - } + public double whiteScore(final Long roomId) { + Round round = round(roomId); + return round.getWhitePlayer().calculateScore(); } - public MoveResponseDto move(final MoveRequestDto moveRequestDto) { - Queue commands = - new ArrayDeque<>(Arrays.asList("move", moveRequestDto.getSource(), moveRequestDto.getTarget())); - round.execute(commands); - movePiece(moveRequestDto); - return new MoveResponseDto(true); + public double blackScore(final Long roomId) { + Round round = round(roomId); + return round.getBlackPlayer().calculateScore(); } - public void movePiece(final MoveRequestDto moveRequestDto) { - chessRepository.removePiece(moveRequestDto); - chessRepository.movePiece(moveRequestDto); + private Round round(final Long roomId) { + Map pieces = pieces(roomId); + String currentTurn = currentTurn(roomId); + return Round.of(ChessBoardFactory.loadBoard(pieces), currentTurn); } - public void changeTurn(final TurnChangeRequestDto turnChangeRequestDto) { - chessRepository.changeTurn(turnChangeRequestDto); + private Map pieces(final Long roomId) { + return chessRepository.findAllPieces(roomId); } - public void initialize() { - StringChessBoardDto filteredChessBoard = filteredChessBoard(); - chessRepository.initializePieceStatus(filteredChessBoard.getStringChessBoard()); - chessRepository.initializeTurn(); + public String currentTurn(final Long roomId) { + return chessRepository.findCurrentTurn(roomId); } - public StringChessBoardDto filteredChessBoard() { - ChessBoardDto chessBoard = chessBoard(); - Map filteredChessBoard = new LinkedHashMap<>(); - for (Map.Entry chessBoardEntry : chessBoard.getChessBoard().entrySet()) { - filterChessBoard(filteredChessBoard, chessBoardEntry); - } - return new StringChessBoardDto(filteredChessBoard); + public void movePiece(final String source, final String target) { + chessRepository.removePiece(target); + chessRepository.movePiece(source, target); } - private void filterChessBoard(final Map filteredChessBoard, - final Map.Entry chessBoardEntry) { - if (chessBoardEntry.getValue() != null) { - filteredChessBoard.put(chessBoardEntry.getKey().toString(), chessBoardEntry.getValue().getPiece()); - } + public void changeTurn(final String nextTurn, final Long roomId) { + chessRepository.changeTurn(nextTurn, roomId); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f5f6f33623..c3f77bf077 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.h2.console.enabled=true spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:13306/mydb?serverTimezone=UTC&characterEncoding=UTF-8 +spring.datasource.url=jdbc:mysql://localhost:13306/mychess?serverTimezone=UTC&characterEncoding=UTF-8 spring.datasource.username=da-nyee spring.datasource.password=1234 spring.datasource.initialization-mode=always diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000000..9efdb2ac97 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS `mychess`.`room` ( + `room_id` BIGINT NOT NULL AUTO_INCREMENT, + `room_name` VARCHAR(45) NOT NULL, + `current_turn` VARCHAR(45) NOT NULL, + PRIMARY KEY (`room_id`) +) CHARSET = utf8, ENGINE = InnoDB; + +CREATE TABLE IF NOT EXISTS `mychess`.`piece` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `piece_name` VARCHAR(45) NOT NULL, + `piece_position` VARCHAR(45) NOT NULL, + `room_id` BIGINT NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`room_id`) + REFERENCES room(`room_id`) +) CHARSET = utf8, ENGINE = InnoDB; \ No newline at end of file diff --git a/src/main/resources/static/css/chess.css b/src/main/resources/static/css/chess.css index ac93778871..af07200829 100644 --- a/src/main/resources/static/css/chess.css +++ b/src/main/resources/static/css/chess.css @@ -47,12 +47,8 @@ body { background-color: cornflowerblue; } -#status-btn { - margin-top: 10px; - width: 325px; -} - -#reset-btn { +#status-btn, +#home-btn { margin-top: 10px; width: 325px; } \ No newline at end of file diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index 1b2debe981..f931d8f843 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -28,6 +28,7 @@ body { margin-top: -80px; } -#start-btn { +#start-btn, +#rooms-btn { width: 300px; } \ No newline at end of file diff --git a/src/main/resources/static/css/rooms.css b/src/main/resources/static/css/rooms.css new file mode 100644 index 0000000000..ecd0c4a141 --- /dev/null +++ b/src/main/resources/static/css/rooms.css @@ -0,0 +1,39 @@ +@font-face { + font-family: 'SDSamliphopangche_Outline'; + src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts-20-12@1.0/SDSamliphopangche_Outline.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + +body { + font-family: 'SDSamliphopangche_Outline', serif !important; +} + +#main-div { + display: grid; + place-items: center; +} + +#start-out-div { + position: absolute; + left: 50%; + top: 50%; + width: 300px; + height: 160px; +} + +#start-in-div { + position: absolute; + margin-left: -150px; + margin-top: -220px; +} + +.room { + display: inline; + font-size: 16pt; +} + +#home-btn { + width: 300px; + margin-top: 14px; +} \ No newline at end of file diff --git a/src/main/resources/static/js/chess.js b/src/main/resources/static/js/chess.js index 0d35a1984f..ac382cd170 100644 --- a/src/main/resources/static/js/chess.js +++ b/src/main/resources/static/js/chess.js @@ -1,5 +1,4 @@ -const jsonFormatChessBoard = document.getElementById('jsonFormatChessBoard'); -const jsonFormatObject = JSON.parse(jsonFormatChessBoard.innerText); +const roomId = document.getElementById("roomId").innerText; const file = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; const rank = ['1', '2', '3', '4', '5', '6', '7', '8']; @@ -11,123 +10,127 @@ for (let i = 0; i < file.length; i++) { } } -for (let i = 0; i < cells.length; i++) { - if (jsonFormatObject[cells[i]]) { - const divCell = document.getElementById(cells[i]); - let piece = jsonFormatObject[cells[i]]; - - const img = document.createElement('img'); - img.style.width = '100%'; - img.style.height = '100%'; - - if (piece === 'P') { - piece = ''; - img.src = '/images/black-pawn.png'; - img.id = divCell.id; - } - if (piece === 'R') { - piece = ''; - img.src = '/images/black-rook.png'; - img.id = divCell.id; - } - if (piece === 'N') { - piece = ''; - img.src = '/images/black-knight.png'; - img.id = divCell.id; - } - if (piece === 'B') { - piece = ''; - img.src = '/images/black-bishop.png'; - img.id = divCell.id; - } - if (piece === 'Q') { - piece = ''; - img.src = '/images/black-queen.png'; - img.id = divCell.id; - } - if (piece === 'K') { - piece = ''; - img.src = '/images/black-king.png'; - img.id = divCell.id; - } - if (piece === 'p') { - piece = ''; - img.src = '/images/white-pawn.png'; - img.id = divCell.id; - } - if (piece === 'r') { - piece = ''; - img.src = '/images/white-rook.png'; - img.id = divCell.id; - } - if (piece === 'n') { - piece = ''; - img.src = '/images/white-knight.png'; - img.id = divCell.id; - } - if (piece === 'b') { - piece = ''; - img.src = '/images/white-bishop.png'; - img.id = divCell.id; - } - if (piece === 'q') { - piece = ''; - img.src = '/images/white-queen.png'; - img.id = divCell.id; - } - if (piece === 'k') { - piece = ''; - img.src = '/images/white-king.png'; - img.id = divCell.id; - } - - divCell.appendChild(img); - } -} - -const currentTurn = document.getElementById('currentTurn'); -const currentTurnP = document.getElementById('current-turn'); -currentTurnP.textContent = currentTurn.innerText; - -const statusBtn = document.getElementById('status-btn'); - -statusBtn.addEventListener('click', function () { - const whiteScore = document.getElementById('whiteScore'); - const blackScore = document.getElementById('blackScore'); - alert('하얀색 기물 점수는: ' + whiteScore.textContent + '\n' + - '검정색 기물 점수는: ' + blackScore.textContent); -}); - -const resetBtn = document.getElementById('reset-btn'); - -resetBtn.addEventListener('click', function () { - window.location.href = '/reset'; -}); - let is_start_position_clicked = false; let start_position = null; let destination = null; let first_click; let second_click; -const pieceCells = document.getElementsByClassName('piece-cell'); +runChessGame(); + +async function runChessGame() { + let boardInfo = await boardDto(); + + const chessBoard = boardInfo['chessBoard']; + const currentTurn = boardInfo['currentTurn']; + + for (let i = 0; i < cells.length; i++) { + if (chessBoard[cells[i]]) { + const divCell = document.getElementById(cells[i]); + let piece = chessBoard[cells[i]]['piece']; + + const img = document.createElement('img'); + img.style.width = '100%'; + img.style.height = '100%'; + + if (piece === 'P') { + piece = ''; + img.src = '/images/black-pawn.png'; + img.id = divCell.id; + } + if (piece === 'R') { + piece = ''; + img.src = '/images/black-rook.png'; + img.id = divCell.id; + } + if (piece === 'N') { + piece = ''; + img.src = '/images/black-knight.png'; + img.id = divCell.id; + } + if (piece === 'B') { + piece = ''; + img.src = '/images/black-bishop.png'; + img.id = divCell.id; + } + if (piece === 'Q') { + piece = ''; + img.src = '/images/black-queen.png'; + img.id = divCell.id; + } + if (piece === 'K') { + piece = ''; + img.src = '/images/black-king.png'; + img.id = divCell.id; + } + if (piece === 'p') { + piece = ''; + img.src = '/images/white-pawn.png'; + img.id = divCell.id; + } + if (piece === 'r') { + piece = ''; + img.src = '/images/white-rook.png'; + img.id = divCell.id; + } + if (piece === 'n') { + piece = ''; + img.src = '/images/white-knight.png'; + img.id = divCell.id; + } + if (piece === 'b') { + piece = ''; + img.src = '/images/white-bishop.png'; + img.id = divCell.id; + } + if (piece === 'q') { + piece = ''; + img.src = '/images/white-queen.png'; + img.id = divCell.id; + } + if (piece === 'k') { + piece = ''; + img.src = '/images/white-king.png'; + img.id = divCell.id; + } + + divCell.appendChild(img); + } + } -for (let i = 0; i < pieceCells.length; i++) { - pieceCells[i].addEventListener('click', (event) => { - event.target.style.backgroundColor = 'gold'; - if (!is_start_position_clicked) { - start_position = event.target.id; - is_start_position_clicked = true; - first_click = event.target; - return; + const current_turn = document.getElementById('current-turn'); + current_turn.innerText = currentTurn; + + const pieceCells = document.getElementsByClassName('piece-cell'); + + for (let i = 0; i < pieceCells.length; i++) { + pieceCells[i].addEventListener('click', (event) => { + event.target.style.backgroundColor = 'gold'; + if (!is_start_position_clicked) { + start_position = event.target.id; + is_start_position_clicked = true; + first_click = event.target; + return; + } + destination = event.target.id; + second_click = event.target; + request_move_post(first_click, second_click, currentTurn); + }); + } +} + +async function boardDto() { + let boardDto = await fetch('/api/chess/' + roomId, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' } - destination = event.target.id; - second_click = event.target; - request_move_post(first_click, second_click); }); + boardDto = await boardDto.json(); + return boardDto; } -function request_move_post(first_click, second_click) { +function request_move_post(first_click, second_click, current_turn) { if (!(cells.indexOf(start_position) && cells.indexOf(destination))) { alert('클릭 오류입니다!'); start_position = null; @@ -138,12 +141,13 @@ function request_move_post(first_click, second_click) { const moveXhr = new XMLHttpRequest(); const turnXhr = new XMLHttpRequest(); - moveXhr.open('POST', '/move', true); + moveXhr.open('POST', '/api/move', true); moveXhr.setRequestHeader('Content-Type', 'application/json'); moveXhr.responseType = 'json'; moveXhr.send(JSON.stringify({ source: start_position, - target: destination + target: destination, + roomId: roomId })); start_position = null; @@ -160,7 +164,6 @@ function request_move_post(first_click, second_click) { return; } - let current_turn = document.getElementById('currentTurn').innerText; let next_turn; if (current_turn === 'white') { @@ -170,13 +173,29 @@ function request_move_post(first_click, second_click) { next_turn = 'white'; } - turnXhr.open('POST', '/turn', true); + turnXhr.open('POST', '/api/turn', true); turnXhr.setRequestHeader('Content-Type', 'application/json'); turnXhr.responseType = 'json'; turnXhr.send(JSON.stringify({ currentTurn: current_turn, - nextTurn: next_turn + nextTurn: next_turn, + roomId: roomId })); - window.location.href = 'http://127.0.0.1:8080/chess'; + window.location.href = '/chess/' + roomId; }; } + +const statusBtn = document.getElementById('status-btn'); + +statusBtn.addEventListener('click', function () { + const whiteScore = document.getElementById('whiteScore'); + const blackScore = document.getElementById('blackScore'); + alert('하얀색 기물 점수는: ' + whiteScore.textContent + '\n' + + '검정색 기물 점수는: ' + blackScore.textContent); +}); + +const homeBtn = document.getElementById('home-btn'); + +homeBtn.addEventListener('click', function () { + window.location.href = "/"; +}); \ No newline at end of file diff --git a/src/main/resources/static/js/home.js b/src/main/resources/static/js/home.js index dd3248a3ef..dd68f9c9cb 100644 --- a/src/main/resources/static/js/home.js +++ b/src/main/resources/static/js/home.js @@ -1,5 +1,28 @@ const startBtn = document.getElementById('start-btn'); +const roomsBtn = document.getElementById('rooms-btn'); -startBtn.addEventListener('click', function () { - window.location.href = '/start'; +startBtn.addEventListener('click', addRoom); + +async function addRoom() { + let roomName = prompt("방 이름을 입력해주세요.", ""); + + if (roomName.length === 0) { + alert("방 이름은 1자 이상으로 입력해주세요!"); + return; + } + let roomIdDto = await fetch('/api/room', { + method: 'POST', + body: JSON.stringify({ + roomName: roomName + }), + headers: { + 'Content-Type': 'application/json' + } + }) + roomIdDto = await roomIdDto.json(); + window.location.href = "/chess/" + roomIdDto.roomId; +} + +roomsBtn.addEventListener('click', function () { + window.location.href = '/rooms'; }); \ No newline at end of file diff --git a/src/main/resources/static/js/rooms.js b/src/main/resources/static/js/rooms.js new file mode 100644 index 0000000000..5d74432c65 --- /dev/null +++ b/src/main/resources/static/js/rooms.js @@ -0,0 +1,15 @@ +const roomNames = document.getElementsByClassName('roomNames'); + +for (let i = 0; i < roomNames.length; i++) { + roomNames[i].addEventListener('click', (event) => { + const roomNameId = event.target.id; + const roomId = roomNameId.substr(4); + window.location.href = '/chess/' + roomId; + }) +} + +const homeBtn = document.getElementById('home-btn'); + +homeBtn.addEventListener('click', function () { + window.location.href = '/'; +}); \ No newline at end of file diff --git a/src/main/resources/templates/chess.hbs b/src/main/resources/templates/chess.hbs index 4c7777e65c..450a87aac1 100644 --- a/src/main/resources/templates/chess.hbs +++ b/src/main/resources/templates/chess.hbs @@ -87,14 +87,11 @@
- + - {{#jsonFormatChessBoard}} - - {{/jsonFormatChessBoard}} - {{#currentTurn}} - - {{/currentTurn}} + {{#roomId}} + + {{/roomId}} {{#score}} diff --git a/src/main/resources/templates/home.hbs b/src/main/resources/templates/home.hbs index d181f3198e..b447e7f841 100644 --- a/src/main/resources/templates/home.hbs +++ b/src/main/resources/templates/home.hbs @@ -1,20 +1,22 @@ - - - 체스 게임 - - - - -
-

✨ 체스 게임 ✨

-
-
- + + + 체스 게임 + + + + +
+

✨ 체스 게임 ✨

+
+
+ +

+ +
-
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/rooms.hbs b/src/main/resources/templates/rooms.hbs new file mode 100644 index 0000000000..8e6902bbf9 --- /dev/null +++ b/src/main/resources/templates/rooms.hbs @@ -0,0 +1,28 @@ + + + + 체스 게임 + + + + +
+

✨ 체스 게임 ✨

+ +
+
+ {{#rooms}} +
+
{{roomId}}
+      +
{{roomName}}
+

+
+ {{/rooms}} +
+
+
+ + + \ No newline at end of file diff --git a/src/test/java/chess/controller/ChessApiControllerTest.java b/src/test/java/chess/controller/ChessApiControllerTest.java new file mode 100644 index 0000000000..27620eb135 --- /dev/null +++ b/src/test/java/chess/controller/ChessApiControllerTest.java @@ -0,0 +1,103 @@ +package chess.controller; + +import chess.domain.piece.King; +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import chess.dto.request.MoveRequestDto; +import chess.dto.request.RoomNameRequestDto; +import chess.dto.request.TurnChangeRequestDto; +import chess.service.ChessService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest +public class ChessApiControllerTest { + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private ChessService chessService; + + @DisplayName("게임을 시작할 때 방을 생성한다.") + @Test + void start() throws Exception { + RoomNameRequestDto roomNameRequestDto = new RoomNameRequestDto("우테코"); + + given(chessService.addRoom("우테코")) + .willReturn(1L); + + mockMvc.perform(post("/api/room") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(roomNameRequestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("roomId").value(1L)); + } + + @DisplayName("DB에서 체스 보드를 가져온다.") + @Test + void chess() throws Exception { + Map board = new HashMap<>(); + board.put(Position.valueOf("1", "e"), King.from("k", Position.valueOf("1", "e"))); + board.put(Position.valueOf("2", "b"), Pawn.from("p", Position.valueOf("2", "b"))); + + given(chessService.board(1L)) + .willReturn(board); + + mockMvc.perform(get("/api/chess/" + 1L) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("chessBoard.*", hasSize(2))); + } + + @DisplayName("기물을 움직인다.") + @Test + void move() throws Exception { + MoveRequestDto moveRequestDto = new MoveRequestDto("a2", "a4", 1L); + + given(chessService.move("a2", "a4", 1L)) + .willReturn(true); + + mockMvc.perform(post("/api/move") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(moveRequestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("movable").value(true)); + } + + @DisplayName("게임이 진행되면 턴이 변경된다.") + @Test + void turn() throws Exception { + TurnChangeRequestDto turnChangeRequestDto = + new TurnChangeRequestDto("white", "black", 1L); + + mockMvc.perform(post("/api/turn") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(turnChangeRequestDto))) + .andExpect(status().isOk()); + + verify(chessService, times(1)) + .changeTurn("black", 1L); + } +}