< 每日算法 - Javascript解析:经典弹珠游戏 >

文章介绍了JavaScript编程中的一道算法问题——弹珠游戏。游戏涉及一个N*M的弹珠盘,包含“O”(洞)、“W”(逆时针转向器)、“E”(顺时针转向器)和“.”(空白区域)四种地形。玩家需要在边缘空白区域打入弹珠,使其最多前进num步后入洞。文章提供了题意解析、解题思路以及部分代码实现,展示如何找到所有能使弹珠最终入洞的打入位置。
摘要由CSDN通过智能技术生成

在这里插入图片描述

一、任务描述:

欢迎各位来到「力扣嘉年华」,接下来将为各位介绍在活动中广受好评的弹珠游戏。

N*M 大小的弹珠盘的初始状态信息记录于一维字符串型数组 plate 中,数组中的每个元素为仅由 “O”、“W”、“E”、“.” 组成的字符串。其中:

  • “O” 表示弹珠洞(弹珠到达后会落入洞中,并停止前进);
  • “W” 表示逆时针转向器(弹珠经过时方向将逆时针旋转 90 度);
  • “E” 表示顺时针转向器(弹珠经过时方向将顺时针旋转 90 度);
  • “.” 表示空白区域(弹珠按原方向通行)。

游戏规则要求仅能在边缘位置的 空白区域 处(弹珠盘的四角除外)沿 与边缘垂直 的方向打入弹珠,并且打入后的每颗弹珠最多能 前进 num 步。请返回符合上述要求且可以使弹珠最终入洞的所有打入位置。

注意

  1. 你可以 按任意顺序 返回答案。
  2. 若弹珠已到达弹珠盘边缘并且仍沿着出界方向继续前进,则将直接出界。

本题取自 👉 leetcode 👈 秋赛题集

》 示例一:

输入: num = 4 plate = ["..E.",".EOW","..W."]

输出:[[2,1]]

弹珠走动路线: 在 [2,1] 处打入弹珠,弹珠前进 1 步后遇到转向器,前进方向顺时针旋转 90 度,再前进 1 步进入洞中。

效果如下图所示:
在这里插入图片描述

》示例二

输入: num = 5 plate = [".....","..E..",".WO..","....."]

输出:[[0,1],[1,0],[2,4],[3,2]]

弹珠走动路线

  1. 在 [0,1] 处打入弹珠,弹珠前进 2 步,遇到转向器后前进方向逆时针旋转 90 度,再前进 1 步进入洞中。
  2. 在 [1,0] 处打入弹珠,弹珠前进 2 步,遇到转向器后前进方向顺时针旋转 90 度,再前进 1 步进入洞中。
  3. 在 [2,4] 处打入弹珠,弹珠前进 2 步后进入洞中。
  4. 在 [3,2] 处打入弹珠,弹珠前进 1 步后进入洞中。

效果如下图所示:
在这里插入图片描述

二、题意解析

示例图


地形解析如下

  • ① “ O ” 洞口,当弹珠坐标 map[Y][X] === 'O' 时,当次初始坐标弹射的弹珠能够成功抵达洞口。
  • ② “ E ” 地形,使弹珠下次移动方向变化,向原方向的顺时针90°方向移动。如: 原方向为: 上(自下而上)遇到 E地形时,下次将向着 右(自左向右)移动。
  • ③ “ W ” 地形,使弹珠下次移动方向变化,向原方向的逆时针90°方向移动。如: 原方向为: 下(自上而下)遇到 W地形时,下次将向着 右(自左向右)移动。
  • ④ “ . ” 地形,不影响弹珠方向,弹珠下次前进按照原定方向前行。

根据题目,可以思考得出以下条件和限制:

  1. 根据示例可以把传入的 plate 理解成一个倒置的直角坐标轴,也就是一个平面。该地图上,存在 4 种类型的地形,不同地形带来的效果不同。

  2. 弹珠的初始位置规定,只能在地图的边缘空白区域 " . " 发射且不能为地图的四个角,以上面的图片为例,初始点为如上蓝色框框的区域。

  3. 弹珠走向判定,这个是这道算法题的一个小难点。 根据题目,我们可以知道地形对弹珠的影响是以 顺/逆时针 为判定的, 所以,我们需要对方向进行顺序排列且对不同方向前进时,YX坐标的加减情况进行枚举。如下图所示:
    在这里插入图片描述

解题思路①

根据题目可知,由于路径是唯一的,一个入口只会对应一个唯一的出口;一个入口+弹珠进入出口的方向由坐标固定的,可以找到唯一的入口。

因此,指需要筛选出,所有符合规定的出发点,按照指定的弹珠移动逻辑。模拟弹珠行走,即可计算出成功进洞弹珠的入口。

具体逻辑和条件规则,上面也详细讲解了。主要是弹珠移动方向和对应地形造成移动方向变更的问题。

三、解决方案:

该解决方案仅供参考,并非最优解。可以自行发掘,理解算法思维最重要!

/**
 * @param {number} num
 * @param {string[]} plate
 * @return {number[][]}
 */
var ballGame = function(num, plate) {
    // 地图数组
    let _map = plate.map(item => item.split(''))
    // 弹珠位置
    let marbles_Y = Infinity
    let marbles_X = Infinity

    // 地图边界
    let Y_Len = _map.length -1
    let X_Len = _map[0].length -1

    let successInitPoint = []
    let direction = null
    // 对应顺时针: 上右下左的YX加减情况
    let directionEnum = [[-1, 0], [0, 1], [1, 0], [0, -1]]

    for(let Y = 0; Y < Y_Len + 1; Y++) {
        for(let X = 0; X < X_Len + 1; X++) {
            // 限定初始点位置
            if(
                _map[Y][X] == '.' &&
                (Y == 0 || X == 0 || Y == Y_Len || X == X_Len) &&
                [Y, X].toString() != [0, 0].toString() &&
                [Y, X].toString() != [Y_Len, 0].toString() &&
                [Y, X].toString() != [0, X_Len].toString() &&
                [Y, X].toString() != [Y_Len, X_Len].toString()
            ) {
                // 定义弹珠位置
                marbles_Y = Number(Y)
                marbles_X = Number(X)
                // 定义初始方向
                if(Y == 0) direction = 2
                else if(Y == (Y_Len)) direction = 0
                else if(X == 0) direction = 1
                else if(X == (X_Len)) direction = 3

                // 定义步数
                let steps = num
                while(_map[marbles_Y][marbles_X] !== 'O') {
                    if(steps === 0) break
                    
                    // 限定踩到 EW 的逻辑
                    // E为顺时针90度,对应上面定义的枚举值,为索引+1且不能超出索引,故取余数
                    if(_map[marbles_Y][marbles_X] == 'E') direction =  (direction + 1) % 4
                    // W为逆时针90度,对应上面定义的枚举值, 相当于走完一圈再-1(4-1=3),为索引+3且不能超出索引,故取余数
                    else if(_map[marbles_Y][marbles_X] == 'W') direction =  (direction + 3) % 4

                    // 根据上面枚举的方向加减值
                    marbles_Y += Number(directionEnum[direction][0])
                    marbles_X += Number(directionEnum[direction][1])

                    // 判断是否超出边界
                    if(marbles_Y < 0 || marbles_Y > Y_Len || marbles_X < 0 || marbles_X > X_Len) break
                    // 判断结束时, 该弹珠是否成功进洞或者超出边界
                    else if(_map[marbles_Y][marbles_X] === 'O') {
                        successInitPoint.push([Y, X])
                        break
                    }
                    steps -= 1
                }
            } else continue
        }
    }

    // console.log(_map)
    return successInitPoint
};

往期内容 💨

🔥 < 每日算法 - Javascript解析: 交通枢纽 >

🔥 < CSS小技巧:filter滤镜妙用>

🔥 < JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

🔥 < 每日技巧: JavaScript代码优化 >

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术宅小温

你小小的鼓励,是我搬砖的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值