原生js简单实现五指棋V2.0(sse + springboot 实现局域网双人对战)

局域网双人五子棋在线对战
在这里插入图片描述

前端代码:

<div id="bigBackground">
	<!-- 棋盘水平线背景板 -->
	<div id="background1"></div>
</div>

<div id="bigBackground">
<!-- 棋盘竖直线背景板 -->
	<div id="background2"></div>
</div>

<div id="bigBackground">
<!-- 棋子背景板 -->
	<div id="black">
		<div id="blackContent">
			<div id="blackContentLog"></div>黑方
		</div>
		<div id="blackMessage"></div>
	</div>
	<div id="background3"></div>
	<div id="blue">
		<div id="blueContent">
			<div id="blueContentLog"></div>白方
		</div>
		<div id="blueMessage"></div>
	</div>
</div>

<script>
	var connectId = '39bd662b7942418595c21a1ef0af7fad';
	getSseConnetcion(connectId);
	const httpRequest = new XMLHttpRequest();
	//游戏是否开始
    var started = false;
	var user = 'user1';
	
	//游戏是否结束
	var isOver = false;
	
	const message = '请落子<div>!</div>';
	const messageWin = '恭喜<div>,</div>你赢了<div>!</div>';

	document.getElementById('blackMessage').innerHTML = message;
	//上次出棋方
	var lastMover = null; // 0代表黑方,1代表蓝方
	var background1 = document.getElementById('background1');
	var content = '';
	//绘制棋盘竖直线
	for (i = 0; i < 32; i++) {
		content += '<div class="horizon"></div>';
	}
	background1.innerHTML = content;
	
	var background2 = document.getElementById('background2');
	content = '';
	//绘制棋盘竖直线
	for (i = 0; i < 32; i++) {
		content += '<div class="vertical"></div>';
	}
	background2.innerHTML = content;
	
	var background3 = document.getElementById('background3');
	content = '';
	//绘制棋盘竖直线
	for (i = 0; i < 1024; i++) {
		content += '<div id="' + Math.ceil((i+1)/32) + ',' + ((i+1)%32==0?32:(i+1)%32) + '" class="cell" onClick="moves()"></div>';
	}
	background3.innerHTML = content;
	
	//绑定落子事件
	for (i = 0; i < 1024; i++) {
		var x = Math.ceil((i+1)/32);
		var y = ((i+1)%32==0?32:(i+1)%32);
		let id = x + ',' + y;
		let cell = document.getElementById(id);
		cell.onclick = function(){
			moves(id);
			nextStep(user, id, connectId);
		}
	}
	
	//落子事件
	function moves (id) {
		if (isOver) {
			return;
		}
	
		let cell = document.getElementById(id);
		if (!started) {
			cell.style.cssText='background-color: black;'
			started = true;
			lastMover = 0;
			document.getElementById('blackMessage').innerHTML = '';
			document.getElementById('blueMessage').innerHTML = message;
			return;
		}
		
		if (lastMover == 0) {
			document.getElementById('blackMessage').innerHTML = message;
			document.getElementById('blueMessage').innerHTML = '';
		} else if (lastMover == 1) {
			document.getElementById('blackMessage').innerHTML = '';
			document.getElementById('blueMessage').innerHTML = message;
		}
		
		if (cell.style.cssText != "") {
			return;
		}
		if (lastMover == 0) {
			cell.style.cssText='background-color: white;'
			lastMover = 1;
		} else {
			cell.style.cssText='background-color: black;'
			lastMover = 0;
		}
		

		var coordinate = id.split(',');
		checkWin(coordinate[0], coordinate[1], cell.style.cssText);
	}
	
	//判定输赢
	function checkWin (x, y, color) {
		x = parseInt(x);
		y = parseInt(y);

		//判断水平线(-)上是否形成五子
		let nearCellIds = [];
		let leftNear = y-4;
		if (leftNear <= 0) {
			leftNear = 1;
		}
		let rightNear = y+5;
		if (rightNear > 33) {
			rightNear = 33;
		} 
		for (i = leftNear; i < rightNear; i++) {
			nearCellIds.push(x + ',' + i);
		}
		if (checkLineFiveCell(nearCellIds, color)) {
			return;
		};
		
		//判断竖直线(|)上是否形成五子
		nearCellIds = [];
		let topNear = x-4;
		if (topNear <= 0) {
			topNear = 1;
		}
		let bottomNear= x+5
		if (bottomNear > 33) {
			bottomNear = 33
		}
		for (i = topNear; i < bottomNear; i++) {
			nearCellIds.push(i + ',' + y);
		}
		if (checkLineFiveCell(nearCellIds, color)) {
			return;
		};
		
		//判断右斜线(\)上是否形成五子
		nearCellIds = [];
		for (i = topNear; i < bottomNear; i++) {
			if (y-(x-i) <= 0) {
				continue;
			}
			if (y-(x-i) > 32) {
				break;
			}
			nearCellIds.push(i + ',' + (y-(x-i)));
		}
		if (checkLineFiveCell(nearCellIds, color)) {
			return;
		};

		//判断左斜线(/)上是否形成五子
		nearCellIds = [];
		for (i = topNear; i < bottomNear; i++) {
			if (y+(x-i) <= 0) {
				break;
			}
			if (y+(x-i) > 32) {
				continue;
			}
			nearCellIds.push(i + ',' + (y+(x-i)));
		}
		checkLineFiveCell(nearCellIds, color);
	}
	
	//判断直线上是否形成连续同色五子
	function checkLineFiveCell (lineCellIds, color) {
		let seriesColorIds = [];
		for (let id of lineCellIds) {
			let nearColor = document.getElementById(id).style.cssText;
			if (nearColor == color) {
				seriesColorIds.push(id);
				if (seriesColorIds.length == 5) {
					changLineFiveCellToWinColor(seriesColorIds);
					let cell = null;
					if ('background-color: black;' == color) {
						cell = document.getElementById('blackMessage');
						document.getElementById('blackContent').style.cssText='color: gold;';
						document.getElementById('blackContentLog').style.cssText='background-color: gold;';
						document.getElementById('blueMessage').innerHTML = '';
					} else {
						cell = document.getElementById('blueMessage');
						document.getElementById('blueContent').style.cssText='color: gold;';
						document.getElementById('blueContentLog').style.cssText='background-color: gold;';
						document.getElementById('blackMessage').innerHTML = '';
					}
					cell.innerHTML = messageWin;
					cell.style.cssText='color: gold;';
					isOver = true;
					return true;
				}
			} else {
				seriesColorIds = [];
			}
		}
		return false;
	}
	
	//改变连续同色五子为胜利色
	function changLineFiveCellToWinColor (lineCellIds) {
		for (let id of lineCellIds) {
			let cell = document.getElementById(id);
			cell.style.cssText='background-color: gold;'
		}
	}
	
	//console.log(cell.getAttribute('id'));
	
	function getSseConnetcion(connectId) {
		if (!!window.EventSource) {
			// 建立连接
			source = new EventSource('http://localhost:8081/xxl-job-executor/sse/createSseConnect?connectId=' + connectId);
			/**
			 * 连接一旦建立,就会触发open事件
			 * 另一种写法:source.onopen = function (event) {}
			 */
			source.addEventListener('open', function (e) {
				setMessageInnerHTML("建立连接...");
			}, false);

			/**
			 * 客户端收到服务器发来的数据
			 * 另一种写法:source.onmessage = function (event) {}
			 */
			source.addEventListener('message', function (e) {
				setMessageInnerHTML(e.data);
				moves(e.data);
			});


			/**
			 * 如果发生通信错误(比如连接中断),就会触发error事件
			 * 或者:
			 * 另一种写法:source.onerror = function (event) {}
			 */
			source.addEventListener('error', function (e) {
				if (e.readyState === EventSource.CLOSED) {
					setMessageInnerHTML("连接关闭");
				} else {
					console.log(e);
				}
			}, false);

		} else {
			setMessageInnerHTML("你的浏览器不支持SSE");
		}
	}
	// 将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
		console.log(innerHTML);
    }
	
	function nextStep(user, xy, connectId) {
        //source.close();
        httpRequest.open('POST', 'http://localhost:8081/xxl-job-executor/sse/nextStep', true);
		httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        httpRequest.send('user=' + user + '&xyCoordinate=' + xy + '&connectId=' + connectId);
		httpRequest.onreadystatechange = function () {
         if (httpRequest.readyState === 4 && httpRequest.status === 200) {  //固定写法
             //数据获取成功,获取服务器响应的数据 
             console.log(httpRequest.responseText)
         }
     }
        console.log("close");
    }
</script>

<style>
body{
	background-color: grey;
}
#bigBackground{
    position: absolute;
    width: 99%;
    height: 694px;
}
#background1{
	width: 692px;
    height: 692px;
    background-color: grey; //#f9f9fa;
    margin: auto;
	border: solid 3px;
}

.horizon{
    width: 693px;
    height: 20px;
    border-bottom: solid 1px;
    margin-left: -1px;
}

#background2{
    width: 692px;
    height: 692px;
    margin: auto;
}

.vertical{
	width: 20px;
    height: 694px;
    border-right: solid 1px;
	display: inline-block;
	margin-top: 1px;
}

#background3{
	width: 672px;
    height: 672px;
    margin: auto;
    margin-top: 10px;
}

.cell{
    width: 20px;
    height: 20px;
	display: inline-block;
	margin-left: 1px;
	margin-top: 1px;
	border-radius: 10px;
}
#black{
	float: left;
    height: 694px;
    width: 400px;
	background-color: #FFFFFF;
}
#blue{
	float: right;
    height: 694px;
    width: 400px;
    position: relative;
    top: -681px;
	background-color: black;
}
#blackContent{
    margin-top: 10px;
	width: 135px;
    margin: auto;
    height: 50px;
    font-size: 40px;
}
#blackContentLog{
	width: 40px;
    height: 40px;
    display: inline-block;
    background-color: black;
    padding-top: -8px;
    position: relative;
    top: 6px;
    border-radius: 20px;
    margin-right: 10px;
}
#blueContent{
    margin-top: 10px;
	width: 135px;
    margin: auto;
    height: 50px;
    font-size: 40px;
	color: #FFFFFF;
}
#blueContentLog{
	width: 40px;
    height: 40px;
    display: inline-block;
    background-color: #FFFFFF;
    padding-top: -8px;
    position: relative;
    top: 6px;
    border-radius: 20px;
    margin-right: 10px;
}
#blackMessage, #blueMessage{
	margin: auto;
    display: block;
    margin-top: 50px;
    width: 25px;
    font-size: 20px;
}
#blueMessage{
	color: #FFFFFF;
}
</style>

后端代码:

package com.xxl.job.executor.mvc.service.impl;

import com.xxl.job.executor.mvc.dao.SseDao;
import com.xxl.job.executor.mvc.po.SseGomoku;
import com.xxl.job.executor.mvc.service.SseService;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xuxueli 2019-05-04 22:13:264
 */
@Service
public class SseServiceImpl implements SseService {

    private static Map<String, SseEmitter> sseCache = new ConcurrentHashMap<>();

    @Resource
    private SseDao sseDao;

    @Override
    public SseEmitter createSseConnect(String connectId) {
        // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
        SseEmitter sseEmitter = new SseEmitter(0L);
        sseEmitter.onCompletion(() -> {
            sseCache.remove(connectId);
        });
        sseEmitter.onError(e -> {
            sseCache.remove(connectId);
            e.printStackTrace();
        });
        sseEmitter.onTimeout(() -> {
            sseCache.remove(connectId);
        });

        sseCache.put(connectId, sseEmitter);

        try {
            sseEmitter.send(SseEmitter.event().id(connectId).data("test"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sseEmitter;
    }

    @Override
    public Map<String, Object> nextStep(Map<String, String> param) {
        Map<String,Object> res = new HashMap<>();
        res.put("data", "success");
        String connectId = param.get("connectId");
        synchronized (SseServiceImpl.class) {
            String user = param.get("user");
            SseGomoku sseGomoku = new SseGomoku();
            sseGomoku.setAccount(user);
            sseGomoku.setXyCoordinate(param.get("xyCoordinate"));
            Integer steps = sseDao.getAllSteps();
            int allSteps = Objects.isNull(steps) ? 0 : steps;
            sseGomoku.setSteps(++allSteps);
            sseDao.save(sseGomoku);

            for (String key : sseCache.keySet()) {
                if (!key.equals(connectId)) {
                    SseEmitter sseEmitter = sseCache.get(key);
                    try {
                        sseEmitter.send(param.get("xyCoordinate"));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return res;
    }
}

表结构:
在这里插入图片描述

CREATE TABLE `sse_gomoku` (
                                `id` int(11) NOT NULL AUTO_INCREMENT,
                                `account` varchar(50) NULL COMMENT '账号',
                                `xy_coordinate` varchar(10) NULL COMMENT '落子xy坐标',
                                `steps` int(10) NULL COMMENT '总步数',
                                PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值