websocket netty 五子棋 简化版

在这里插入图片描述在这里插入图片描述在这里插入图片描述

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'io.netty:netty-all:4.1.36.Final'
    implementation 'org.projectlombok:lombok:1.18.16'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

socket 服务


@Slf4j
@Component
public class NettyWebSocketApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // 创建主线程池组,处理客户端的连接(多反应器)
        NioEventLoopGroup mainGroup = new NioEventLoopGroup();

        // 创建从线程池组,处理客户端的读写(多反应器)
        NioEventLoopGroup subGroup = new NioEventLoopGroup();

        try {
            // 创建netty引导类,配置和串联系列组件(设置线程模型,设置通道类型,设置客户端处理器handler,设置绑定端口号)
            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap.group(mainGroup, subGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel) throws Exception {
                    // 配置链式解码器
                    ChannelPipeline pipeline = channel.pipeline();

                    // 解码成HttpRequest
                   pipeline.addLast(new HttpServerCodec());

                    // 解码成FullHttpRequest   POST
                    pipeline.addLast(new HttpObjectAggregator(1024 * 10));

                    // 添加WebSocket解编码
                    pipeline.addLast(new WebSocketServerProtocolHandler("/"));

                    // 添加处自定义的处理器
                    pipeline.addLast(new TextWebSocketFrameHandler());
                }
            });

            // 异步绑定端口号,需要阻塞住直到端口号绑定成功
            ChannelFuture channelFuture = bootstrap.bind(7397);
            channelFuture.sync();

            log.info("websocket服务端启动成功啦!");

        } catch (InterruptedException e) {
            log.info("{} websocket服务器启动异常", e.getMessage());
        } finally {
//            mainGroup.shutdownGracefully();
//            subGroup.shutdownGracefully();
        }
    }
}

消息


/**
 * 1 连接
 * 0 连接失败
 * 2 开始游戏   2表示白,21表示黑
 * 3 推送游戏信息
 * 4 游戏结束
 */
@Slf4j
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    boolean start = false;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        log.info("消息 {}", msg.text());
        if (!start && "2".equals(msg.text())) {
            for (Channel c : channels) {
                if (ctx.channel() != c) {
                    c.writeAndFlush(new TextWebSocketFrame("21:游戏开始"));
                }
            }
            start = true;
        } else {
            for (Channel c : channels) {
                if (ctx.channel() != c) {
                    c.writeAndFlush(new TextWebSocketFrame(msg.text()));
                }
            }

            if (msg.text().startsWith("4:")) {
                start = false;
                for (Channel c : channels) {
                    channels.remove(c);
                }
            }
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        if (channels.size() == 2) {
            ctx.channel().writeAndFlush(new TextWebSocketFrame("0:已有人在对弈中。。"));
        } else if (channels.size() == 1) {
            channels.add(ctx.channel());
            channels.writeAndFlush(new TextWebSocketFrame("2:游戏开始"));
        } else if (channels.size() == 0) {
            ctx.channel().writeAndFlush(new TextWebSocketFrame("1:等待人加入可以开始博弈了。。"));
            channels.add(ctx.channel());
        } else {
            ctx.channel().writeAndFlush(new TextWebSocketFrame("0:连接失败。。"));
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        channels.remove(ctx.channel());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("1:等待人加入可以开始博弈了。。"));
        start = false;
    }
}

页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>js画布--五指棋</title>
</head>
<style>
    #now {
        position:absolute;
        width: 30px;
        height: 30px;
        border: 2px solid #e4007e;
        text-align: center;
        line-height: 200px;
        font-weight: bold;
        font-size: 24px;
        background: burlywood;
        border-radius: 50%;
        z-index:100;
    }
</style>
<body>
<div id="content">五指棋</div>
<canvas id="myCanvas" width="800" height="800" style="border:1px solid #c3c3c3;">
    您的浏览器不支持 HTML5 canvas 标签。
</canvas>

<div id="now"></div>
<script>
    const set = new Set();
    //  黑白色
    let bool = true;
    //  当前是否是 本人走棋
    let go = true;
    //  本人棋子
    let b = true;

    const now = document.getElementById("now");
    const t = document.getElementById("content");
    const c = document.getElementById("myCanvas");
    const ctx = c.getContext("2d");

    let websocket = new WebSocket("ws://127.0.0.1:7397");
    websocket.onopen = (evt) => {
        t.innerText = "等待人加入可以开始博弈了";
    };

    websocket.onmessage = (evt) => {
        let split = evt.data.split(":");
        if (split[0] == 2) {
            init();
            websocket.send("2");
            t.innerText = split[1] + ",执白棋  先走";
        } else if (split[0] == 21) {
            init();
            bool = false;
            go = false;
            b = false;
            t.innerText = split[1] + ",执黑棋  后走";
        } else if (split[0] == 3) {
            let s = split[1].split("_");
            ctx.beginPath();
            ctx.arc(s[1] * 40, s[2] * 40, 15, 0, 2 * Math.PI);
            if (s[0] == 1) {
                ctx.fillStyle = "#ffffff";
                now.style.background = "#ffffff";
            } else {
                ctx.fillStyle = "#000000";
                now.style.background = "#000000";
            }
            ctx.fill();

            set.add(split[1]);

            bool = b;
            go = true;

            now.style.left = (c.getBoundingClientRect().left + s[1] * 40 - 15) + "px";
            now.style.top = (c.getBoundingClientRect().top + s[2] * 40 - 15) + "px";

        } else if (split[0] == 4) {
            ctx.font = "60px Georgia";
            ctx.fillStyle = "red";
            ctx.fillText('您输了!!!', 240, 340);

            t.innerText = "等待人加入可以开始博弈了";

        }
    };

    function init() {
        ctx.fillStyle = "#ccc";
        ctx.fillRect(0, 0, 800, 800);

        let i;
        for (i = 40; i < 800; i = i + 40) {
            ctx.beginPath();
            ctx.moveTo(i, 40);
            ctx.lineTo(i, 760);
            ctx.stroke();
        }

        for (i = 40; i < 800; i = i + 40) {
            ctx.beginPath();
            ctx.moveTo(40, i);
            ctx.lineTo(760, i);
            ctx.stroke();
        }
    }

    c.addEventListener('click', (function () {
        //标准的获取鼠标点击相对于canvas画布的坐标公式
        let x = event.pageX - c.getBoundingClientRect().left;
        let y = event.pageY - c.getBoundingClientRect().top;

        if (x < 40 || x > 780 || y < 40 || y > 780 || !go) {
            return;
        }
        go = false;

        x = getLocation(x);
        y = getLocation(y);

        var key = getKey(bool, x, y);
        if (set.has(key) || set.has(getKey(!bool, x, y))) {
            return;
        }

        ctx.beginPath();
        ctx.arc(x, y, 15, 0, 2 * Math.PI);
        if (bool) {
            ctx.fillStyle = "#ffffff";
        } else {
            ctx.fillStyle = "#000000";
        }
        ctx.fill();

        set.add(key);
        websocket.send('3:' + key);

        if (valid(b, x, y)) {
            websocket.send("4:游戏结束");
            ctx.font = "60px Georgia";
            ctx.fillStyle = "red";
            ctx.fillText('您赢了!!!' , 240, 340);
            t.innerText = "等待人加入可以开始博弈了";
        }
        bool = !b;
    }));

    function getLocation(l) {
        const v = l % 40;
        const v1 = Math.floor(l / 40);
        return v > 20.0 ? v1 * 40 + 40 : v1 * 40;
    }

    function getKey(bool, x, y) {
        const i = bool ? 1 : 0;
        x = Math.floor(x / 40);
        y = Math.floor(y / 40);
        return i + "_" + x + "_" + y;
    }

    function getKeyStr(i, x, y) {
        return i + "_" + x + "_" + y;
    }

    function valid(bool, x, y) {
        const r = bool ? 1 : 0;
        x = Math.floor(x / 40);
        y = Math.floor(y / 40);

        const heng = hengValid(r, x, y);
        if (heng) return true;
        const su = suValid(r, x, y);
        if (su) return true;
        const x1 = xieZheng(r, x, y);
        if (x1) return true;
        return xieSu(r, x, y);
    }

    function xieZheng(r, x, y) {
        let str = '';
        for (let i = (x > 4 ? x - 4 : 1); i <= (x > 15 ? 19 : x + 4); i++) {
            str = str + (set.has(getKeyStr(r, i, x + y - i)) ? '1' : '0');
        }
        return str.indexOf("11111") !== -1;
    }

    function xieSu(r, x, y) {
        let str = '';
        for (let i = (y > 4 ? y - 4 : 1); i <= (y > 15 ? 19 : y + 4); i++) {
            str = str + (set.has(getKeyStr(r, x - (y - i), i)) ? '1' : '0');
        }
        return str.indexOf("11111") !== -1;
    }

    function suValid(r, x, y) {
        let str = '';
        for (let i = (y > 4 ? y - 4 : 1); i <= (y > 15 ? 19 : y + 4); i++) {
            str = str + (set.has(getKeyStr(r, x, i)) ? '1' : '0');
        }
        return str.indexOf("11111") !== -1;
    }

    function hengValid(r, x, y) {
        let str = '';
        for (let i = (x > 4 ? x - 4 : 1); i <= (x > 15 ? 19 : x + 4); i++) {
            str = str + (set.has(getKeyStr(r, i, y)) ? '1' : '0');
        }
        return str.indexOf("11111") !== -1;
    }

</script>

</body>
</html>

五子棋 js canvas 校验逻辑

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值