月神之箭(Elune’s Arrow)

来源:互联网 发布:ubuntu deb 编辑:程序博客网 时间:2024/04/28 05:19

月神之箭(Elune’s Arrow)

(10102510352)

问题描述

    在游戏dota(TheDefense of Ancients,暴风雪集团的一款RPG游戏,深受广大青少年的喜爱,本人曾经在一段时间内十分欣赏该款游戏)中,有一个英雄叫月亮之神(以下简称POM),她的第二个招数叫“月神之箭”,这招能给敌人带来巨大的伤害。当POM的箭沿着直线飞出后,击中敌人便会消失。

    题目现在假设,POM躲在树林里,想使用第二招射杀一个很重要的一个敌人。在敌人的周围有其他敌方单位,当POM的箭射错了方向或者是射到了敌方的其他单位,敌人将不会被射中。现在给出一个箭射出的指定方向,我们得去判断这个敌人是否会被集中。

    已知:敌方单位的个数,包括目标敌人(一个整数);POM所在的位置(两个整数,代表POM所在位置的平面坐标);POM的箭射出的方向(两个整数(x0,y0),表示射箭方向的向量);若干个敌人单位(每个敌人的表示方法为:m x1 y1 x2 y2… xm ym,敌方单位为一个凸多边形,m表示多边形的顶点个数,x1 y1,x2 y2…分别表示每个顶点的平面坐标。并且,POM不在多边形内,也不再多边形的边上。

    求解:POM是否能射中目标敌人。能的话,输出“HIT”,不能则输出“MISS”。

解题思路

    这是一道从游戏中挖掘的题目,但是不难看出,这是一道很纯粹的数学几何题。POM是一个点,射箭的方向便是一条射线,敌方单位是多边形,于是题目便可翻译成数学几何题:已知一个定点以及从该点发出的射线,求解该射线能不能从给定的几个多边形中通过。于是,一个游戏题目,就完全可以使用我们所学的数学只是来解答。

我们可以先关注该射线是否能穿过一个多边形这个问题,多个多边形只需要采取同样的方法便可得出结果。先给出一个图:

                                                                                                

                                                                                         

    P0是题目给出的定点,n0是射线的向量,s和r分别表示p0到两个点的向量,不难看出s和r向量在n0的正交向量即P’0上的分量的符号是相反的,也就是说s* P’0与r* P’0的符号是相反的。

    从以上结论出发,我们便很快可以思考出解决该问题的算法。我们只需要遍历多边形的不同的两个顶点,遍历的同时判断该两点与定点P0的向量在射线的正交向量的分向量之积为正数还是负数,如果遍历的时候出现一次负数,则表示射线肯定经过该多边形,于是可以停止遍历。如果全部正数,则表示射线与多边形不相交。当然,如果正好为0,则表示某个顶点在射线的方向上,但是,这个时候必须注意,当定点在射线的相反方向是,结果也等于0,因此不能直接判断该点就在射线上。观察得知,当某个定点在射线上时,另外一个顶点与定点连接的向量与射线向量的积必然为正数,所以据此可以判断为0的情况。

算法与分析

    由解题思路分析得到的结果,可以很快的设计出算法。

    首先,我们需要去遍历每一个敌方单位,第一个遍历的对象为目标敌人,如果目标敌人都无法击中,则表示POM根本无法射中,停止遍历,输出结果为“MISS”,伪代码如下:

for e:enemies[enemyNum]     if 射不中break 停止程序  elsecontinue 继续遍历

    如果能够击中目标敌人,我们也不能立即就输出“HIT”,因为POM也有可能及击中其他单位,于是目标敌人便击不中了。我们下一步需要遍历其他地方单位,如果遍历到有一个敌方单位被击中,则停止遍历,输出“MISS”,表示POM击中了其他敌人。伪代码如下:

for e:otherenemies[enemyNum]     if 射中break 停止程序  elsecontinue 继续遍历

    如果遍历后发现只有目标敌人能被击中,则输出“HIT”,表示目标敌人能够被POM击中。至此,我们所有逻辑都已经确定,但是最重要的判断是否射中的算法还未给出。判断是否射中,主要是去判断每一个敌人的每两个顶点与定点的向量还有射线的正交向量之积是否存在符号相反的情况,如果出现,可立即判断该敌方单位能被射中,如果出现积为0的情况,需要计算顶点与定点连接的向量与射线向量的积是否有一个为正数,如果有,则能够射中,如果不能,则无法射中。具体算法,请看伪代码:

 

for i:vertices[m]  for j:vertices[m]v0 := i与定点连接的向量v1 := j 与定点连接的向量p’0 := 射线的正交向量r := (v0*p’0)*(v1*p’0)if r<0   射中else if r=0   p0 :=射线的正交向量   r1 :=(v0*p0)   r2 :=(v1:p0)   if r1>0 或者r2>0射中    else射不中

至此,所有关键的算法都已经讨论并且实现完毕。

程序说明

1、classPoint

   点对象,该对象有两个类变量,x和y,分别表示点的横坐标和纵坐标

2、classPolygon

   多边形对象,该对象有一个类变量,vertices[],其类型为point数组,即多边形有点的数组表示

3、classVector

   向量对象,该对象有两个类变量,a和b,分别表示向量的横坐标和纵坐标。

4、intenemyNum

   敌方单位的数量

5、PointpomPlace

   POM所在的位置

6、VectorflyDirection

   POM射箭的方向

7、Polygon[]enemy[]

   敌方单位的数组

8、voidmain()

   程序的主函数,也是程序的入口,负责整个程序的逻辑控制,以及核心算法的实现。

9、voidgetInput()

   从一个txt文件中获取输入信息

程序清单

 

package sei.zwf.main;

 

import java.io.BufferedReader;

 

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

 

public class EluneArrow {

                /*the point object*/

                public class Point

                {

                       intx;

                       inty;

                }

                /*polygon object*/

                public class Polygon

                {

                       Point vertices[];

                }

                /*vector object*/

                public class Vector

                {

                       inta;

                       intb;

                }

                /*enemy number*/

                public static int enemyNum;

                /*pom place*/

                public static Point pomPlace =new EluneArrow().new Point();

                /*fly dirction vector*/

                public static Vector flyDirection =new EluneArrow().new Vector();

                /*a enemy */

                public static Polygon enemy[] ;

                public static void main(String args[])

                {

                       new EluneArrow().getInput();

                       Vector flyDirectionZ = new EluneArrow().new Vector();

                       flyDirectionZ.a = -flyDirection.b;

                       flyDirectionZ.b = flyDirection.a;

                       int hitNum = 0;

                       boolean targetHit =false;

                       /*traverse the point of polygon*/

                       for(int e=0;e<enemy.length;e++)

                       {

                              Polygon sigleEnemy = enemy[e];

                              boolean canHit =false;

                              for(inti=0;i<sigleEnemy.vertices.length-1;i++)

                              {

                                     for(intj=i+1;i<sigleEnemy.vertices.length;i++)

                                     {

                                            int tempX =sigleEnemy.vertices[i].x-pomPlace.x;

                                            int tempY =sigleEnemy.vertices[i].y-pomPlace.y;

                                            Vector pomToVertice = new EluneArrow().new Vector();

                                            pomToVertice.a = tempX;

                                            pomToVertice.b = tempY;

    int crossResult0 = pomToVertice.a*flyDirectionZ.a + pomToVertice.b*flyDirectionZ.b;

                                            int tempX1 =sigleEnemy.vertices[j].x-pomPlace.x;

                                            int tempY1 =sigleEnemy.vertices[j].y-pomPlace.y;

                                            Vector pomToVertice1 = new EluneArrow().new Vector();

                                            pomToVertice1.a = tempX1;

                                            pomToVertice1.b = tempY1;

            int crossResult1 = pomToVertice1.a*flyDirectionZ.a + pomToVertice1.b*flyDirectionZ.b;

    /*if the resultis negative,the enemy can be hit*/

                                            if(crossResult0*crossResult1<0)

                                            {

                                                    canHit = true;

                                                    if(e==0)

                                                           targetHit= true;

                                                    break;

                                            }

                                            /*if the result is0,another would be judge the result*/

                                            elseif(crossResult0*crossResult1==0)

                                            {

                                                    int cross0 =pomToVertice.a*flyDirection.a +  pomToVertice.b*flyDirection.b;

    int cross1 = pomToVertice1.a*flyDirection.a + pomToVertice1.b*flyDirection.b;

                                                    if(cross0>0||cross1>0)

                                                    {

                                                           canHit = true;

                                                           if(e==0)

                                                                  targetHit= true;

                                                           break;

                                                    }

                                            }

                                     }

                                     if(canHit)

                                     {

                                            hitNum++;

                                            break;

                                     }

                              }

                              if(!targetHit)

                                     break;

                             

                       }

                       if(hitNum==1&&targetHit)

                              System.out.println("Hit");

                       else

                              System.out.println("Miss");

                }

                /*get the input from a text file*/

                public  void getInput()

                {

                       String filePath = System.getProperty("user.dir")+"/src/sei/zwf/input/eainput.txt";

                       File input = new File(filePath);

                       String enemyNumStr;

                       String pomPlaceStr;

                       String flyDirectionStr;

                       String enemyStr;

                       try {

                              BufferedReader fr = new BufferedReader(newFileReader(input));

                              enemyNumStr = fr.readLine();

                              pomPlaceStr = fr.readLine();

                              flyDirectionStr = fr.readLine();

                              //init enemyNum

                              enemyNum = Integer.parseInt(enemyNumStr);

                              enemy =new Polygon[enemyNum];

                              for(int x=0;x<enemyNum;x++)

                                     enemy[x] =new Polygon();

                              int j=0;

                              while((enemyStr =fr.readLine())!=null)

                              {

                                     //init enemy

                                     String[] enemyData =enemyStr.split(" ");

                                     int verticeNum =Integer.parseInt(enemyData[0]);

                                     enemy[j].vertices =new Point[verticeNum];

                                     for(inti=2;i<=verticeNum*2;i=i+2)

                                     {

                                            Point vertice = new EluneArrow().new Point();

                                            vertice.x = Integer.parseInt(enemyData[i-1]);

                                            vertice.y = Integer.parseInt(enemyData[i]);

                                            enemy[j].vertices[i/2-1] = vertice;

                                     }

                                     j++;

                              }

                              fr.close();

                             

                              //init pomplace

                              String[] pomPlacePointStr =pomPlaceStr.split(" ");

                              pomPlace.x = Integer.parseInt(pomPlacePointStr[0]);

                              pomPlace.y = Integer.parseInt(pomPlacePointStr[1]);

                              //init flyDirection

                              String[] flyDStr =flyDirectionStr.split(" ");

                              flyDirection.a = Integer.parseInt(flyDStr[0]);

                              flyDirection.b = Integer.parseInt(flyDStr[1]);

                             

                             

                       } catch (FileNotFoundException e) {

                              // TODO Auto-generated catch block

                              e.printStackTrace();

                       } catch (IOException e) {

                              // TODO Auto-generated catch block

                              e.printStackTrace();

                       }

                }

 

}

注:转载请注明出处,做一个文明的IT人。
原创粉丝点击