Programmeerimise algkursus Harjutus 3

Allikas: Lambda
Mine navigeerimisribale Mine otsikasti

Esimene töö: valida sihtimiseks siga, mis on meile kõige lähemal.

Idee: teeme radariga tiiru ära ja märgime tiiru jooksul leitud sigadest kõige lähemal olnu kauguse ja peilungi ära. Peale tiiru lõpetamist keerame tuki sea suunas ja anname tina!

Algatuseks peame onScannedRobot sündmusest üleliigse kõntsa ära koristama. Seal istub praegu keeramisnurga arvutamise kood, millest teeme eraldi funktsiooni (meil läheb seda hiljem niikuinii vaja):

    public double arvutaKeeramisnurk(double vastase_peilung) {
        double tuki_suund = getGunHeading();
        double minu_suund = getHeading();

        double vastase_suund = minu_suund + vastase_peilung;
        double keeramisnurk = vastase_suund - tuki_suund;

        return normalRelativeAngle(keeramisnurk);
    }

Teeme globaalmuutujad sea_kaugus ja siga_sihikul. Selleks lisame kaks uut rida oma roboti klassi algusesse:

public class LollRobot extends Robot {

    double siga_sihikul;
    double sea_kaugus;

...

Radari keerutamisel käivitub iga sea kohta onScannedRobot sündmus. Sündmus peab märkima ära kõige lähemal olnud sea.

    public void onScannedRobot(ScannedRobotEvent e) {
        double vastase_kaugus = e.getDistance();
        double vastase_peilung = e.getBearing();

        // Otsime kõige lähemal paiknevat vastast
        if(vastase_kaugus < sea_kaugus) {
            sea_kaugus = vastase_kaugus;
            siga_sihikul = vastase_peilung;
        }
    }

Tuki keeramine ja laskmine hakkavad toimuma peale radari "scan"-i. Modifitseerime run meetodit vastavalt:

 
    double laksuTugevus = 2.0;

    public void run() {
        // Ära keera tukki koos kerega ega radarit koos tukiga!
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);

        while(true) {
            // Enne otsimist on lähim siga lõpmata kaugel
            sea_kaugus = Double.MAX_VALUE;

            // Radari scan
            turnRadarLeft(360);

            // Keerame tuki sea suunda ja anname tina!
            double keeramisnurk = arvutaKeeramisnurk(siga_sihikul);
            turnGunRight(keeramisnurk);
            fire(laksuTugevus);
        }
    }

Teine töö: arvutada sea sibamiskiiruse järgi ettelaskmisnurk

Meie praegune robot laseb sinna, kus ta siga nägi. Inglise keeles nimetatakse seda Head-On targeting.

Kuna kuuli lendamine võtab oma aja, siis liikuvat siga pole alati mõtet sihtida sinna, kus ta parasjagu on, vaid natuke temast ette poole. Põhimõtteliselt saame liikumiskiiruse (loe: kohvipaksu) pealt "ennustades" arvutada, kus vastane kuuli kohalejõudmise ajaks tegelikult olla võiks ja suunata oma toru sinna.

Joonistame kolmnurga, mille ühes tipus olen mina (R) teises tipus on vastane (s) ja kolmandas tipus vastase asukoht kuuli kohalejõudmise ajal (s'). Kolmnurga üks külg on teada: vastase kaugus. Teised kaks saab hõlpsasti arvutada kuuli lennuaja ja kiiruste järgi.

                                        R
                                       /|
                                      /a|
     kuuli kiirus * kuuli lennuaeg   /  |
                                    /   |  
                                   /____|
                                  s'    s
                                         
                    vastase kiirus * kuuli lennuaeg

Natuke trigonomeetriat meelde tuletades avastame, et ettelaskmisnurga saab arvutada:

ettelaskmisnurk = asin(vastase kiirus / kuuli kiirus)

Kuuli lennuaeg taandub mõnusasti valemist välja. Järelikult on nurga arvutamiseks vaja teada ainult vastase kiirust ja kuuli lennukiirust.

Robocode "füüsikareeglid"[1] annavad kuuli lennukiiruse arvutamiseks järgmise valemi:

kuuli kiirus = 20 - 3 * laksu tugevus

Vastase kursi ja liikumiskiiruse saab küsida funktsioonidega e.getHeading() ja e.getVelocity().

Paraku ei ole asi päris nii lihtne: meil on vaja teada vastase kiiruse seda komponenti, mis sunnib meid tema sihtimiseks toru keerama, teisisõnu: meid ei huvita kui kiiresti ta meie poole või meist eemale sõidab. Nö "vasakule-paremale" kiirust meie suhtes nimetatakse peenelt lateraalkiiruseks [2].

Lateraalkiiruse siht on alati täpselt risti vastase peilungiga. Oletame, et vastane liikus punktis s punkti s'. Lateraalkiiruse saamiseks tuleb korrutada kiirus (vektori pikkus) kiirusvektori ja peilungi vahelise nurga siinusega.

                 
                                        s
                                       /|
                                      /a|
                      kiirusvektor   /  |  peilung
                                    /   |  
                                   /____|
                                  s'  
                               lateraalkiirus

Kiirusvektori ja peilungi vahelise nurga saamiseks lahutame vastase kursist (liikumise suunast) maha vastase suuna (peilung absoluutkoordinaadistikus, ehk minu suund + peilung minu suuna suhtes).

vastase suund = peilung + minu suund
lateraalkiirus = kiirus * sin(vastase kurss - vastase suund)

Nüüd on meil olemas kõik vajalik, et kirjutada funktsioon ettelaskmisnurga arvutamisks. Kuna aga java.Math pakis leiduvad trigonomeetrilised funktsioonid kasutavad radiaane, tuleb kraadid enne arvutamist radiaanideks konverteerida ja pärast vastupidi tagasi. Abiks on funktsioonid Math.toDegrees(nurk) ja Math.toRadians(nurk).

    public double arvutaEttelaskmisnurk(ScannedRobotEvent e) {
        double vastase_peilung = Math.toRadians(e.getBearing());
        double vastase_kurss = Math.toRadians(e.getHeading());
        double vastase_kiirus = e.getVelocity();
        double minu_suund = Math.toRadians(getHeading());
        double kuuli_kiirus = 20 - 3 * laksuTugevus;

        double vastase_suund = minu_suund + vastase_peilung;

        double lateraalkiirus = vastase_kiirus * Math.sin( vastase_kurss - vastase_suund );

        return Math.toDegrees(Math.asin(lateraalkiirus / kuuli_kiirus));
    }

Jääb veel üle integreerida ettelaskmisnurga arvutamine robotisse.

Uus globaalne muutuja: siga_ettelase

public class LollRobot extends Robot {

   double siga_sihikul;
   double sea_kaugus;
   double siga_ettelase;

...

Lähima sea jaoks ettelaskmisnurga arvutamine onScannedRobot meetodis:

   public void onScannedRobot(ScannedRobotEvent e) {
       double vastase_kaugus = e.getDistance();
       double vastase_peilung = e.getBearing();

       // Otsime kõige lähemal paiknevat vastast
       if(vastase_kaugus < sea_kaugus) {
           sea_kaugus = vastase_kaugus;
           siga_sihikul = vastase_peilung;
           siga_ettelase = arvutaEttelaskmisnurk(e);
       }
   }

Ettelaskmisnurga kasutamine tuki keeramisel run meetodis:

   double laksuTugevus = 2.0;

   public void run() {
       // Ära keera tukki koos kerega ega radarit koos tukiga!
       setAdjustGunForRobotTurn(true);
       setAdjustRadarForGunTurn(true);

       while(true) {
           // Enne otsimist on lähim siga lõpmata kaugel
           sea_kaugus = Double.MAX_VALUE;

           // Radari scan
           turnRadarLeft(360);

           // Keerame tuki sea suunda ja anname tina!
           double keeramisnurk = arvutaKeeramisnurk(siga_sihikul);
           keeramisnurk = normalRelativeAngle(keeramisnurk + siga_ettelase);
           turnGunRight(keeramisnurk);
           fire(laksuTugevus);
       }
   }

Iseseisev töö

Saavutatud robot on ikka veel vigane. Nimelt laseb ta natuke mööda. Põhjuseks on fakt, et radari keeramine võtab aega ja tulistamist ei alustata enne, kui radariga on täisring ära tehtud. Mida rohkem jäi vastase avastamise hetkest radarit keerata, seda ebatäpsem on lask. Selle vea parandamiseks tuleb laskmine tõsta tagasi onScannedRobot meetodisse. Teie ülesanne on saada sellega hakkama nii, et laskmiseks lähima roboti valimine tööle jääks.

Lisalugemist

Käsitletud sihtimismismeetod on äärmiselt primitiivne:

  • Eeldab, et vastane liigub sirgjooneliselt (nn lineaarne sihtimine)
  • Ei arvesta fakti, et vastane ei saa sõita läbi seina

Samuti ei ole mõtet lasta kogu aeg ühesuguse tugevusega kuulidega. Turniiril võitmiseks on vaja midagi natuke "targemat". Võiduhimulised loevad neid lehekülgi ja teevad omad järeldused: