오늘 우리는 서블릿과 JSP를 사용하여 Tic-Tac-Toe 게임을 작성할 것입니다.
이 프로젝트는 이전 프로젝트와 조금 다를 것입니다. 작업뿐만 아니라 수행 방법에 대한 설명도 포함됩니다. 즉, "HOW TO ..."시리즈의 프로젝트가 될 것입니다.
지침:
- 저장소에서 포크: https://github.com/CodeGymCC/project-servlet.git
- 프로젝트 버전을 컴퓨터에 다운로드합니다.
- IDEA에서 애플리케이션 실행 설정:
- Alt + Shift + F9 -> 구성 편집… -> Alt + 삽입 -> tom(검색 표시줄에) -> Local.
- 그런 다음 "구성"을 클릭하고 Tomcat이 있는 아카이브를 다운로드하고 압축을 푼 위치를 지정해야 합니다.
- "배포" 탭에서: Alt + 삽입 -> Artifact… -> tic-tac-toe:war exploded -> 확인.
- "응용 프로그램 컨텍스트" 필드에서 "/"(슬래시)만 남겨 둡니다.
- "적용"을 누르십시오.
- 설정 창을 닫습니다.
- 사용자 정의된 구성의 첫 번째 테스트 실행을 수행하십시오. 모든 것이 올바르게 완료되면 다음과 같은 기본 브라우저가 열립니다.
- "pom.xml" 파일을 엽니다 . "종속성" 블록 에는 2개의 종속성이 있습니다 .
javax.servlet-api
서블릿 사양을 담당합니다. "제공된" 범위는 개발 중에 필요하지만 런타임에는 필요하지 않습니다(Tomcat은 이미 lib 폴더에 이 종속성을 가지고 있습니다).jstl
– 템플릿 엔진으로 간주할 수 있습니다.- "webapp" 폴더 에는 3개의 파일이 있습니다 .
index.jsp
- 이것은 템플릿입니다(HTML 페이지와 유사). 여기에는 마크업과 스크립트가 포함됩니다. 3단계에서 보았던 구성이 없을 경우 초기 페이지로 주어지는 것은 “index” 라는 파일입니다 ./static/main.css
- 스타일 파일. 이전 프로젝트에서와 마찬가지로 여기에 있는 모든 것은 원하는 대로 페인트할 수 있습니다./static/jquery-3.6.0.min.js
- 서버가 정적으로 배포할 프런트엔드 종속성.- "com.tictactoe" 패키지에는 모든 Java 코드가 포함됩니다. 현재 2개의 클래스가 있습니다.
Sign
- enum은 "cross/zero/void"를 담당합니다 .Field
우리 분야입니다. 이 클래스에는 "필드" 맵이 있습니다 . 데이터 저장 원리는 다음과 같습니다. tic-tac-toe 필드의 셀은 0부터 번호가 매겨집니다. 첫 번째 줄에는 0, 1, 2, 두 번째 줄에는 3, 4, 5 등이 있습니다. 3가지 방법도 있습니다. "getEmptyFieldIndex"는 첫 번째 빈 셀을 찾습니다(예, 상대방은 그다지 똑똑하지 않습니다). "checkWin"은 게임이 끝났는지 확인합니다. 세 개의 십자 표시가 있는 행이 있으면 십자 표시를 반환하고 3개의 0이 있는 행이 있으면 0을 반환합니다. 그렇지 않으면 비어 있습니다. "getFieldData" - "필드" 맵 의 값을 인덱스 오름차순으로 정렬된 목록으로 반환합니다.- 템플릿에 대한 설명이 끝났습니다. 이제 작업을 시작할 수 있습니다. 3 x 3 테이블을 그리는 것으로 시작하겠습니다. 이렇게 하려면 "index.jsp" 에 다음 코드를 추가합니다 .
그런 다음 테이블에서 숫자를 제거하고 십자, 0 또는 빈 필드로 바꿉니다. 또한 "head" 태그 안에 스타일 파일을 포함합니다. 이렇게 하려면 다음 행을 추가하십시오.<table> <tr> <td>0</td> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> <td>5</td> </tr> <tr> <td>6</td> <td>7</td> <td>8</td> </tr> </table>
<link href="static/main.css" rel="stylesheet">
스타일 파일의 내용은 귀하에게 달려 있습니다. 나는 이것을 사용했다 :
실행 후 내 결과는 다음과 같습니다.td { border: 3px solid black; padding: 10px; border-collapse: separate; margin: 10px; width: 100px; height: 100px; font-size: 50px; text-align: center; empty-cells: show; }
- 이제 다음 기능을 추가해 보겠습니다. 셀을 클릭하면 클릭한 셀의 인덱스를 매개변수로 전달하는 요청이 서버로 전송됩니다. 이 작업은 전면에서 요청을 보내고 서버에서 요청을 수락하는 두 부분으로 나눌 수 있습니다. 변화를 위해 정면에서 시작합시다.
각 "d" 태그 에 "onclick" 매개변수를 추가해 보겠습니다 . 값에서 현재 페이지가 지정된 URL로 변경되었음을 나타냅니다. 논리를 담당할 서블릿에는 URL "/logic" 이 있습니다 . 그리고 "click" 이라는 매개변수를 사용합니다 . 따라서 사용자가 클릭한 셀의 인덱스를 전달합니다.
브라우저의 개발자 패널을 통해 모든 것이 올바르게 수행되었는지 확인할 수 있습니다. 예를 들어 Chrome에서는 F12 버튼 으로 열립니다 . 인덱스가 4인 셀을 클릭한 결과 그림은 다음과 같습니다. "logic" 주소로 서버를 보낼 수 있는 서블릿을 아직 생성하지 않았기 때문에 오류가 발생합니다 .<table> <tr> <td onclick="window.location='/logic?click=0'">0</td> <td onclick="window.location='/logic?click=1'">1</td> <td onclick="window.location='/logic?click=2'">2</td> </tr> <tr> <td onclick="window.location='/logic?click=3'">3</td> <td onclick="window.location='/logic?click=4'">4</td> <td onclick="window.location='/logic?click=5'">5</td> </tr> <tr> <td onclick="window.location='/logic?click=6'">6</td> <td onclick="window.location='/logic?click=7'">7</td> <td onclick="window.location='/logic?click=8'">8</td> </tr> </table>
- "com.tictactoe" 패키지에서 "javax.servlet.http.HttpServlet " 클래스에서 파생되어야 하는 "LogicServlet" 클래스를 만듭니다 . 클래스에서 "doGet" 메서드를 재정의합니다 .
그리고 클릭한 셀의 인덱스를 가져오는 메서드를 추가해 보겠습니다. 또한 매핑(이 서블릿이 요청을 가로챌 주소)을 추가해야 합니다. 주석을 통해 이 작업을 수행하는 것이 좋습니다(어려움이 마음에 들면 web.xml을 사용할 수도 있음). 일반 서블릿 코드:
이제 임의의 셀을 클릭하면 서버에서 이 셀의 인덱스를 가져옵니다(디버그에서 서버를 실행하여 확인할 수 있음). 클릭이 발생한 동일한 페이지로 리디렉션됩니다.package com.tictactoe; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "LogicServlet", value = "/logic") public class LogicServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int index = getSelectedIndex(req); resp.sendRedirect("/index.jsp"); } private int getSelectedIndex(HttpServletRequest request) { String click = request.getParameter("click"); boolean isNumeric = click.chars().allMatch(Character::isDigit); return isNumeric ? Integer.parseInt(click) : 0; } }
- 이제 클릭할 수 있지만 아직 게임이 아닙니다. 게임에 논리가 있으려면 요청 사이에 게임 상태(십자가 있는 곳, 0이 있는 곳)를 저장해야 합니다. 이를 수행하는 가장 쉬운 방법은 이 데이터를 세션에 저장하는 것입니다. 이 접근 방식을 사용하면 세션이 서버에 저장되고 클라이언트는 "JSESSIONID" 라는 쿠키에 세션 ID를 받습니다 . 그러나 세션은 매번 생성할 필요가 없으며 게임 시작 시에만 생성됩니다. 이를 위해 "InitServlet" 이라고 하는 다른 서블릿을 시작하겠습니다 . "doGet" 메서드를 재정의하여 새 세션을 만들고, 경기장을 만들고, 이 경기장과 Sign 유형 목록을 세션 속성에 넣고 " forward" 를 index.jsp로 보냅니다. 페이지. 암호:
잊지 말고 서버를 시작한 후 브라우저에서 열리는 시작 페이지를 "/start" 로 변경하십시오 . 이제 서버를 다시 시작하고 "요청 헤더" 섹션의 브라우저 개발자 메뉴에서 필드의 아무 셀이나 클릭한 후 , 세션 ID가 있는 쿠키가 있습니다.package com.tictactoe; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; import java.util.Map; @WebServlet(name = "InitServlet", value = "/start") public class InitServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Create a new session HttpSession currentSession = req.getSession(true); // Create a playing field Field field = new Field(); Map<Integer, Sign> fieldData = field.getField(); // Get a list of field values List<Sign> data = field.getFieldData(); // Adding field parameters to the session (needed to store state between requests) currentSession.setAttribute("field", field); // and field values sorted by index (required for drawing crosses and zeroes) currentSession.setAttribute("data", data); // Redirect request to index.jsp page via server getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp); } }
- 클라이언트(브라우저)의 요청 간에 상태를 저장할 수 있는 저장소가 있으면 게임 로직 작성을 시작할 수 있습니다. 우리가 가지고 있는 로직은 "LogicServlet" 에 있습니다 . "doGet" 메서드 로 작업해야 합니다 . 이 동작을 메서드에 추가해 보겠습니다.
- 우리는 세션에서 Field 유형의 "field" 개체를 가져올 것입니다( "extractField" 메서드 로 가져갈 것입니다 ).
- 사용자가 클릭한 곳에 십자 표시를 합니다(지금까지 확인하지 않음).
동작은 아직 변경되지 않았지만 디버그에서 서버를 시작하고 리디렉션이 전송되는 줄에 중단점을 설정하면 "data" 객체 의 "innards"를 볼 수 있습니다 . 실제로 클릭한 색인 아래에 "CROSS"가 표시됩니다 .@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Get the current session HttpSession currentSession = req.getSession(); // Get the playfield object from the session Field field = extractField(currentSession); // get the index of the cell that was clicked int index = getSelectedIndex(req); // put a cross in the cell that the user clicked on field.getField().put(index, Sign.CROSS); // Read the list of icons List<Sign> data = field.getFieldData(); // Update field object and icon list in session currentSession.setAttribute("data", data); currentSession.setAttribute("field", field); resp.sendRedirect("/index.jsp"); } private Field extractField(HttpSession currentSession) { Object fieldAttribute = currentSession.getAttribute("field"); if (Field.class != fieldAttribute.getClass()) { currentSession.invalidate(); throw new RuntimeException("Session is broken, try one more time"); } return (Field) fieldAttribute; }
- 이제 프런트엔드에 십자가를 표시할 차례입니다. 이를 위해 "index.jsp" 파일 과 "JSTL" 기술을 사용합니다 .
- <head> 섹션에 다음을 추가합니다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 각 <td> 블록 내부의 테이블에서 인덱스를 값을 계산할 수 있는 구문으로 변경합니다. 예를 들어 인덱스 0의 경우:
<td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td>
이제 셀을 클릭하면 십자 표시가 나타납니다.
- <head> 섹션에 다음을 추가합니다.
- 우리는 움직였습니다. 이제 "제로"의 차례입니다. 그리고 이미 점유된 셀에 표지판이 배치되지 않도록 여기에 몇 가지 확인 사항을 추가해 보겠습니다.
- 클릭한 셀이 비어있는지 확인해야 합니다. 그렇지 않으면 아무것도 하지 않고 세션 매개변수를 변경하지 않고 사용자를 동일한 페이지로 보냅니다.
- 필드의 셀 수가 홀수이므로 십자가가 배치되었을 가능성이 있지만 0이 들어갈 여지가 없습니다. 따라서 십자 표시를 한 후 비어 있는 셀의 인덱스를 가져오려고 합니다(Field 클래스의 getEmptyFieldIndex 메서드). 인덱스가 음수가 아니면 거기에 0을 넣으십시오. 암호:
package com.tictactoe; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; @WebServlet(name = "LogicServlet", value = "/logic") public class LogicServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Get the current session HttpSession currentSession = req.getSession(); // Get the playfield object from the session Field field = extractField(currentSession); // get the index of the cell that was clicked int index = getSelectedIndex(req); Sign currentSign = field.getField().get(index); // Check if the clicked cell is empty. // Otherwise, we do nothing and send the user to the same page without changes // parameters in the session if (Sign.EMPTY != currentSign) { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp"); dispatcher.forward(req, resp); return; } // put a cross in the cell that the user clicked on field.getField().put(index, Sign.CROSS); // Get an empty field cell int emptyFieldIndex = field.getEmptyFieldIndex(); if (emptyFieldIndex >= 0) { field.getField().put(emptyFieldIndex, Sign.NOUGHT); } // Read the list of icons List<Sign> data = field.getFieldData(); // Update field object and icon list in session currentSession.setAttribute("data", data); currentSession.setAttribute("field", field); resp.sendRedirect("/index.jsp"); } private int getSelectedIndex(HttpServletRequest request) { String click = request.getParameter("click"); boolean isNumeric = click.chars().allMatch(Character::isDigit); return isNumeric ? Integer.parseInt(click) : 0; } private Field extractField(HttpSession currentSession) { Object fieldAttribute = currentSession.getAttribute("field"); if (Field.class != fieldAttribute.getClass()) { currentSession.invalidate(); throw new RuntimeException("Session is broken, try one more time"); } return (Field) fieldAttribute; } }
- 이 단계에서 십자 표시를 하면 AI가 0으로 대답합니다. 그러나 언제 게임을 중지할지 확인하지 않습니다. 다음 세 가지 경우일 수 있습니다.
- 다음 십자가 이동 후 세 개의 십자가 선이 형성되었습니다.
- 0으로 다음 리턴 이동 후 세 개의 0이 형성되었습니다.
- 십자가의 다음 이동 후 빈 셀이 끝났습니다.
이 방법의 특이점은 승자가 발견되면 세션에 다른 매개변수를 추가하여 다음 단락에서 "index.jsp" 의 표시를 변경한다는 것입니다 ./** * The method checks if there are three X/O's in a row. * returns true/false */ private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException { Sign winner = field.checkWin(); if (Sign.CROSS == winner || Sign.NOUGHT == winner) { // Add a flag to indicate that someone has won currentSession.setAttribute("winner", winner); // Read the list of icons List<Sign> data = field.getFieldData(); // Update this list in session currentSession.setAttribute("data", data); // helmet redirect response.sendRedirect("/index.jsp"); return true; } return false; }
- “checkWin ” 메서드에 대한 호출을 “doGet” 메서드에 두 번 추가해 봅시다 . 십자가를 설정 한 후 처음으로, 두 번째는 0을 설정 한 후입니다.
// Check if the cross won after adding the user's last click if (checkWin(resp, currentSession, field)) { return; }
if (emptyFieldIndex >= 0) { field.getField().put(emptyFieldIndex, Sign.NOUGHT); // Check if the zero won after adding the last zero if (checkWin(resp, currentSession, field)) { return; } }
- 동작 측면에서 거의 아무것도 변경되지 않았습니다(사인 중 하나가 이기면 더 이상 0이 배치되지 않는다는 점만 제외). "index.jsp" 에서 "winner" 매개변수를 사용 하고 승자를 표시해 보겠습니다. 테이블
c:set
다음에 지시문을 사용합니다.c:if
크로스가 이기면 "CROSSES WIN!" , 0이 "NOUGHTS WIN!" 인 경우 . 결과적으로 다음 두 비문 중 하나를 얻을 수 있습니다.<hr> <c:set var="CROSSES" value="<%=Sign.CROSS%>"/> <c:set var="NOUGHTS" value="<%=Sign.NOUGHT%>"/> <c:if test="${winner == CROSSES}"> <h1>CROSSES WIN!</h1> </c:if> <c:if test="${winner == NOUGHTS}"> <h1>NOUGHTS WIN!</h1> </c:if>
- 승자가 있으면 복수할 수 있어야 합니다. 이렇게하려면 서버에 요청을 보내는 버튼이 필요합니다. 그리고 서버는 현재 세션을 무효화하고 요청을 "/start" 로 다시 리디렉션합니다 .
- "head" 섹션 의 "index.jsp" 에서 스크립트 "jquery" 를 작성합니다 . 이 라이브러리를 사용하여 서버에 요청을 보냅니다.
<script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script>
- "script" 섹션 의 "index.jsp" 에서 서버에 POST 요청을 보낼 수 있는 기능을 추가합니다. 함수를 동기식으로 만들고 서버에서 응답이 오면 현재 페이지를 다시 로드합니다.
<script> function restart() { $.ajax({ url: '/restart', type: 'POST', contentType: 'application/json;charset=UTF-8', async: false, success: function () { location.reload(); } }); } </script>
- "c:if" 블록 안에 클릭하면 방금 작성한 함수를 호출하는 버튼을 추가합니다.
<c:if test="${winner == CROSSES}"> <h1>CROSSES WIN!</h1> <button onclick="restart()">Start again</button> </c:if> <c:if test="${winner == NOUGHTS}"> <h1>NOUGHTS WIN!</h1> <button onclick="restart()">Start again</button> </c:if>
- "/restart" URL을 제공할 새 서블릿을 만들어 봅시다 .
승리 후 "다시 시작" 버튼이 나타납니다 . 그것을 클릭하면 필드가 완전히 지워지고 게임이 다시 시작됩니다.package com.tictactoe; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "RestartServlet", value = "/restart") public class RestartServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.getSession().invalidate(); resp.sendRedirect("/start"); } }
- "head" 섹션 의 "index.jsp" 에서 스크립트 "jquery" 를 작성합니다 . 이 라이브러리를 사용하여 서버에 요청을 보냅니다.
- 마지막 상황을 고려하는 것이 남아 있습니다. 사용자가 십자가를 놓으면 승리가 없고 0이 들어갈 자리가 없다면? 그러면 이것은 무승부이며 지금 처리합니다.
- "LogicServlet" 세션 에서 다른 매개변수 "draw"를 추가하고 "data" 필드를 업데이트한 다음 "index.jsp" 로 리디렉션을 보냅니다 .
// If such a cell exists if (emptyFieldIndex >= 0) { … } // If there is no empty cell and no one wins, then it's a draw else { // Add a flag to the session that signals that a draw has occurred currentSession.setAttribute("draw", true); // Read the list of icons List<Sign> data = field.getFieldData(); // Update this list in session currentSession.setAttribute("data", data); // helmet redirect response.sendRedirect("/index.jsp"); return; }
- "index.jsp" 에서 이 매개변수를 처리합니다.
무승부의 결과로 해당 메시지와 다시 시작하라는 제안을 받게 됩니다.<c:if test="${draw}"> <h1>IT'S A DRAW</h1> <br> <button onclick="restart()">Start again</button> </c:if>
- "LogicServlet" 세션 에서 다른 매개변수 "draw"를 추가하고 "data" 필드를 업데이트한 다음 "index.jsp" 로 리디렉션을 보냅니다 .
이것으로 게임 작성이 완료되었습니다.
작업한 클래스 및 파일 코드
초기화 서블릿
package com.tictactoe;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@WebServlet(name = "InitServlet", value = "/start")
public class InitServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Create a new session
HttpSession currentSession = req.getSession(true);
// Create a playing field
Field field = new Field();
Map<Integer, Sign> fieldData = field.getField();
// Get a list of field values
List<Sign> data = field.getFieldData();
// Adding field parameters to the session (needed to store state between requests)
currentSession.setAttribute("field", field);
// and field values sorted by index (required for drawing crosses and zeroes)
currentSession.setAttribute("data", data);
// Redirect request to index.jsp page via server
getServletContext().getRequestDispatcher("/index.jsp").forward(req, resp);
}
}
로직서블릿
package com.tictactoe;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
@WebServlet(name = "LogicServlet", value = "/logic")
public class LogicServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Get the current session
HttpSession currentSession = req.getSession();
// Get the playfield object from the session
Field field = extractField(currentSession);
// get the index of the cell that was clicked
int index = getSelectedIndex(req);
Sign currentSign = field.getField().get(index);
// Check if the clicked cell is empty.
// Otherwise, we do nothing and send the user to the same page without changes
// parameters in the session
if (Sign.EMPTY != currentSign) {
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp");
dispatcher.forward(req, resp);
return;
}
// put a cross in the cell that the user clicked on
field.getField().put(index, Sign.CROSS);
// Check if the cross has won after adding the user's last click
if (checkWin(resp, currentSession, field)) {
return;
}
// Get an empty field cell
int emptyFieldIndex = field.getEmptyFieldIndex();
if (emptyFieldIndex >= 0) {
field.getField().put(emptyFieldIndex, Sign.NOUGHT);
// Check if the zero won after adding the last zero
if (checkWin(resp, currentSession, field)) {
return;
}
}
// If there is no empty cell and no one wins, then it's a draw
else {
// Add a flag to the session that signals that a draw has occurred
currentSession.setAttribute("draw", true);
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update this list in session
currentSession.setAttribute("data", data);
// helmet redirect
resp.sendRedirect("/index.jsp");
return;
}
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update field object and icon list in session
currentSession.setAttribute("data", data);
currentSession.setAttribute("field", field);
resp.sendRedirect("/index.jsp");
}
/**
* The method checks if there are three X/O's in a row.
* returns true/false
*/
private boolean checkWin(HttpServletResponse response, HttpSession currentSession, Field field) throws IOException {
Sign winner = field.checkWin();
if (Sign.CROSS == winner || Sign.NOUGHT == winner) {
// Add a flag to indicate that someone has won
currentSession.setAttribute("winner", winner);
// Read the list of icons
List<Sign> data = field.getFieldData();
// Update this list in session
currentSession.setAttribute("data", data);
// helmet redirect
response.sendRedirect("/index.jsp");
return true;
}
return false;
}
private int getSelectedIndex(HttpServletRequest request) {
String click = request.getParameter("click");
boolean isNumeric = click.chars().allMatch(Character::isDigit);
return isNumeric ? Integer.parseInt(click) : 0;
}
private Field extractField(HttpSession currentSession) {
Object fieldAttribute = currentSession.getAttribute("field");
if (Field.class != fieldAttribute.getClass()) {
currentSession.invalidate();
throw new RuntimeException("Session is broken, try one more time");
}
return (Field) fieldAttribute;
}
}
RestartServlet
package com.tictactoe;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "RestartServlet", value = "/restart")
public class RestartServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.getSession().invalidate();
resp.sendRedirect("/start");
}
}
index.jsp_ _
<%@ page import="com.tictactoe.Sign" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<link href="static/main.css" rel="stylesheet">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<script src="<c:url value="/static/jquery-3.6.0.min.js"/>"></script>
<title>Tic-Tac-Toe</title>
</head>
<body>
<h1>Tic-Tac-Toe</h1>
<table>
<tr>
<td onclick="window.location='/logic?click=0'">${data.get(0).getSign()}</td>
<td onclick="window.location='/logic?click=1'">${data.get(1).getSign()}</td>
<td onclick="window.location='/logic?click=2'">${data.get(2).getSign()}</td>
</tr>
<tr>
<td onclick="window.location='/logic?click=3'">${data.get(3).getSign()}</td>
<td onclick="window.location='/logic?click=4'">${data.get(4).getSign()}</td>
<td onclick="window.location='/logic?click=5'">${data.get(5).getSign()}</td>
</tr>
<tr>
<td onclick="window.location='/logic?click=6'">${data.get(6).getSign()}</td>
<td onclick="window.location='/logic?click=7'">${data.get(7).getSign()}</td>
<td onclick="window.location='/logic?click=8'">${data.get(8).getSign()}</td>
</tr>
</table>
<hr>
<c:set var="CROSSES" value="<%=Sign.CROSS%>"/>
<c:set var="NOUGHTS" value="<%=Sign.NOUGHT%>"/>
<c:if test="${winner == CROSSES}">
<h1>CROSSES WIN!</h1>
<button onclick="restart()">Start again</button>
</c:if>
<c:if test="${winner == NOUGHTS}">
<h1>NOUGHTS WIN!</h1>
<button onclick="restart()">Start again</button>
</c:if>
<c:if test="${draw}">
<h1>IT'S A DRAW</h1>
<button onclick="restart()">Start again</button>
</c:if>
<script>
function restart() {
$.ajax({
url: '/restart',
type: 'POST',
contentType: 'application/json;charset=UTF-8',
async: false,
success: function () {
location.reload();
}
});
}
</script>
</body>
</html>
메인 .css_
td {
border: 3px solid black;
padding: 10px;
border-collapse: separate;
margin: 10px;
width: 100px;
height: 100px;
font-size: 50px;
text-align: center;
empty-cells: show;
}
GO TO FULL VERSION