Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: PHP: Rubikin kuutio

Sivun loppuun

Antti Laaksonen [05.01.2005 17:07:03]

#

Rubikin kuution tietokoneversioita on tehty maailman sivu, harvemmin kuitenkaan PHP:llä. Tässä tulee nettisivulla toimiva hiirellä pyöriteltävä kolmiulotteinen Rubikin kuutio.

Esittelysivu: http://koti.mbnet.fi/pllk/muut/rubik_p.php

Kuution palojen numerointi on:

1.        2.        3.        4.        5.        6.
00 01 02  09 10 11  18 19 20  27 28 29  36 37 38  45 46 47
03 04 05  12 13 14  21 22 23  30 31 32  39 40 41  48 49 50
06 07 08  15 16 17  24 25 26  33 34 35  42 43 44  51 52 53

Sivu 1 on kuvassa oikealla, sivu 2 vasemmalla ja sivu 3 ylhäällä. Sivu 4 on oikean kulman takana, sivu 5 vasemman kulman takana ja sivu 6 on alhaalla. Sivut 1, 3, 5 ja 6 ovat ketjutettuina toisiinsa. Sivut 2 ja 4 ovat vastatusten. Sivu 3 on siis väärinpäin sivuihin 1, 2 ja 4 nähden.

Piirtojärjestys:

1.        2.        3.
00 11 24  03 14 25  06 17 26
01 10 21  04 13 22  07 16 23
02 09 18  05 12 19  08 15 20

Kuutiossa olevat palat on tallennettu yhteen merkkijonoon, jonka pituus on 6 * 9 = 54 merkkiä. Kuution sivuja käsitellään merkkijonoina myös pyörityksissä. Jos vain tiettyä riviä pyöritetään, kuutiota pyöritetään kuitenkin ensin kokonaan ja sitten valitaan uusista paloista pyörivään riviin kuuluvat.

Skriptin monimutkaisimpia osuuksia on oikean palan laskeminen napsautetun kohdan perusteella. Tämä tapahtuu vertaamalla napsautuskohtaa palojen rajojen koordinaatteihin. Erityisen vaikea tapaus on yläsivu, jossa tuloksia saattaa muka olla useampia suorakulmioiden mennessä toistensa päälle. Tällöin oikea tulos saadaan selville laskemalla Pythagoraan lausetta soveltamalla etäisyys palan keskikohtaan ja valitsemalla etäisyyksistä pienin.

Päivitys 6.1.: Kunnollinen sekoitus

rubik_k.php

<?php

// piirtää Rubikin kuution parametrina annetuin värein

$koko = 200; // kuvan koko
$järj = $_SERVER['QUERY_STRING'];

$kuva = imagecreate($koko, $koko);

// määritetään värit:
//  0      valkoinen, taustaväri
//  1 - 6  kuutioiden värit
//  7      musta, reunusväri
for ($i = 7; $i >= 0; $i--) {
    $väri[] = imagecolorallocate($kuva, ($i & 4) * 255,
                                        ($i & 2) * 255,
                                        ($i & 1) * 255);
}

// piirretään kuvan kehykset
imagerectangle($kuva, 0, 0, $koko - 1, $koko - 1, $väri[7]);

$yx = $koko / 2;           // keskikulman x-koordinaatti
$yy = $koko / 2.2;         // keskikulman y-koordinaatti
$kx = .9 * $koko / 3 / 3;  // venytys x-suunnassa
$ky = .9 * $koko / 5 / 3;  // venytys y-suunnassa
$ps = .9 * $koko / 2 / 3;  // päätysivun pituus

for ($i = 0; $i < 3; $i++) {
    for ($j = 0; $j < 3; $j++) {
        // kuution oikean sivun koordinaatit
        $oikea = array($yx + $j * $kx,
                       $yy - $j * $ky + $i * $ps,
                       $yx + $j * $kx,
                       $yy - $j * $ky + ($i + 1) * $ps,
                       $yx + ($j + 1) * $kx,
                       $yy - ($j + 1) * $ky + ($i + 1) * $ps,
                       $yx + ($j + 1) * $kx,
                       $yy - ($j + 1) * $ky + $i * $ps);
        // kuution vasemman sivun koordinaatit
        $vasen = array($yx - $j * $kx,
                       $yy - $j * $ky + $i * $ps,
                       $yx - $j * $kx,
                       $yy - $j * $ky + ($i + 1) * $ps,
                       $yx - ($j + 1) * $kx,
                       $yy - ($j + 1) * $ky + ($i + 1) * $ps,
                       $yx - ($j + 1) * $kx,
                       $yy - ($j + 1) * $ky + $i * $ps);
        // kuution yläsivun koordinaatit
        $ylä   = array($yx - $j * $kx + $i * $kx,
                       $yy - $j * $ky - $i * $ky,
                       $yx - $j * $kx + ($i + 1) * $kx,
                       $yy - $j * $ky - ($i + 1) * $ky,
                       $yx - ($j + 1) * $kx + ($i + 1) * $kx,
                       $yy - ($j + 1) * $ky - ($i + 1) * $ky,
                       $yx - ($j + 1) * $kx + $i * $kx,
                       $yy - ($j + 1) * $ky - $i * $ky);

        // piirretään kuutioiden värilliset sivut
        imagefilledpolygon($kuva, $oikea, 4, $väri[$järj[$kohta]]);
        imagefilledpolygon($kuva, $vasen, 4, $väri[$järj[$kohta + 1]]);
        imagefilledpolygon($kuva, $ylä, 4, $väri[$järj[$kohta + 2]]);

        // piirretään vastaavien sivujen reunukset
        imagepolygon($kuva, $oikea, 4, $väri[7]);
        imagepolygon($kuva, $vasen, 4, $väri[7]);
        imagepolygon($kuva, $ylä, 4, $väri[7]);

        $kohta += 3;
    }
}

header("Content-type: image/png");

imagepng($kuva);

?>

rubik_p.php

<?php

// näyttää pyöritettävän Rubikin kuution

// laskee valitun kuution osan hiiren koordinaateista
function laskekohta($x, $y) {
    // näiden tietojen on oltava samat kuin piirtoskriptissä
    $koko = 200;
    $yx = $koko / 2;
    $yy = $koko / 2.2;
    $kx = .9 * $koko / 3 / 3;
    $ky = .9 * $koko / 5 / 3;
    $ps = .9 * $koko / 2 / 3;

    $pienin = pow($koko, 2); // pienin etäisyys yläsivun paloissa
    $py = false;             // loppupalautusarvo

    // tutkitaan kaikki vaihtoehdot
    for ($i = 0; $i < 3; $i++) {
        for ($j = 0; $j < 3; $j++) {
            // kuution oikean sivun tarkistukset
            if ($yx + $j * $kx <= $x &&
                $yx + ($j + 1) * $kx >= $x &&
                $yy - $j * $ky + $i * $ps <= $y &&
                $yy - ($j + 1) * $ky + ($i + 1) * $ps >= $y) {
                return array("o", $i, $j);
            }
            // kuution vasemman sivun tarkistukset
            if ($yx - $j * $kx >= $x &&
                $yx - ($j + 1) * $kx <= $x &&
                $yy - $j * $ky + $i * $ps <= $y &&
                $yy - ($j + 1) * $ky + ($i + 1) * $ps >= $y) {
                return array("v", $i, $j);
            }
            // kuution yläsivun tarkistukset
            if ($yx - ($j + 1) * $kx + $i * $kx <= $x &&
                $yx - $j * $kx + ($i + 1) * $kx >= $x &&
                $yy - $j * $ky - $i * $ky >= $y &&
                $yy - ($j + 1) * $ky - ($i + 1) * $ky <= $y) {
                // etäisyys palan keskipisteeseen
                $matka = pow($yx - $j * $kx + $i * $kx - $x, 2) * $kx +
                         pow($yy - $j * $ky - $i * $ky - $ky / 2 - $y, 2) * $ky;
                // pienin etäisyys hyväksytään
                if ($matka < $pienin) {
                    $py = array("y", $i, $j);
                    $pienin = $matka;
                }
            }
        }
    }
    return $py;
}

// arpoo kuution palojen järjestyksen
function sekoitakuutio() {
    // aloitustilanne
    $kuutio = "111111111222222222333333333444444444555555555666666666";
    // pyöritysten vaihtoehdot
    $sivut = array("v", "o", "y");
    $akselit = array("x", "y");
    $suunnat = array(-1, 1);
    // pyöritetään sata kertaa satunnaisesta kohdasta
    for ($i = 0; $i < 100; $i++) {
        $kuutio = pyöritäriviä($kuutio, $sivut[rand(0, 2)], $akselit[rand(0, 1)],
                               rand(0, 2), $suunnat[rand(0, 1)]);
    }
    return $kuutio;
}

// kiertää yhtä kuution sivua
function kierrä($sivu, $suunta) {
    $kuvio = array("" => array(0, 1, 2, 3, 4, 5, 6, 7, 8),   // ei kiertoa
                   "v" => array(2, 5, 8, 1, 4, 7, 0, 3, 6),  // vasemmalle
                   "o" => array(6, 3, 0, 7, 4, 1, 8, 5, 2),  // oikealle
                   "y" => array(8, 7, 6, 5, 4, 3, 2, 1, 0)); // ylösalaisin

    foreach($kuvio[$suunta] as $k) {
        $uusi .= $sivu[$k];
    }

    return $uusi;
}

// pyörittää koko kuutiota
function pyöritäkuutiota($kuutio, $x, $y) {
    // ilmoittaa, mikä sivu siirretään minne ja onko kiertoa
    // käännöskohdat: ylävasen, keskivasen, alavasen,
    //                yläoikea, keskioikea, alaoikea
    $suunnat = array(array(array("", 6, "v", 2, "", 1, "o", 4, "", 3, "", 5),
                           array("", 4, "", 1, "o", 3, "y", 5, "y", 2, "v", 6),
                           array("v", 1, "v", 3, "v", 4, "v", 6, "o", 5, "v", 2)),
                     array(array("o", 1, "o", 6, "o", 2, "o", 3, "v", 5, "o", 4),
                           array("", 2, "y", 5, "v", 3, "", 1, "y", 4, "o", 6),
                           array("", 3, "o", 2, "", 5, "v", 4, "", 6, "", 1)));

    // merkkijonon aloituskohta on (sivu - 1) * 9
    for ($i = 0; $i < 6; $i++) {
        $uusi .= kierrä(substr($kuutio, ($suunnat[$x][$y][$i * 2 + 1] - 1) * 9, 9),
                        $suunnat[$x][$y][$i * 2]);
    }

    return $uusi;
}

// ilmoittaa luvun merkin (-1, 0 tai 1)
function merkki($luku) {
    if ($luku != 0) {
        return $luku / abs($luku);
    }
    return 0;
}

// pyörittää valittua riviä
function pyöritäriviä($kuutio, $sivu, $akseli, $rivi, $suunta) {
    // kussakin pyörityksessä muuttuvat palat
    $vanhat = array(array(0, 1, 2, 9, 10, 11, 18, 19, 20, 21, 22, 23,
                          24, 25, 26, 27, 28, 29, 42, 43, 44),
                    array(3, 4, 5, 12, 13, 14, 30, 31, 32, 39, 40, 41),
                    array(6, 7, 8, 15, 16, 17, 45, 46, 47, 48, 49, 50,
                          51, 52, 53, 33, 34, 35, 36, 37, 38),
                    array(0, 3, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17,
                          18, 21, 24, 36, 39, 42, 45, 48, 51),
                    array(1, 4, 7, 19, 22, 25, 37, 40, 43, 46, 49, 52),
                    array(2, 5, 8, 20, 23, 26, 27, 28, 29, 30, 31, 32,
                          33, 34, 35, 38, 41, 44, 47, 50, 53),
                    array(0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 14, 17, 24,
                          25, 26, 27, 30, 33, 45, 46, 47),
                    array(10, 13, 16, 21, 22, 23, 28, 31, 34, 48, 49, 50),
                    array(9, 12, 15, 18, 19, 20, 29, 32, 35, 36, 37,
                          38, 39, 40, 41, 42, 43, 44, 51, 52, 53));
    // oikean sivun pyöritys
    if ($sivu == "o") {
        if ($akseli == "x") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 0 : 1, 1);
            $muutos = $rivi;
        } elseif ($akseli == "y") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 0 : 1,
                                    ($suunta == -1) ? 0 : 2);
            $muutos = 3 + $rivi;
        }
    // vasemman sivun pyöritys
    } elseif ($sivu == "v") {
        if ($akseli == "x") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 1 : 0, 1);
            $muutos = $rivi;
        } elseif ($akseli == "y") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 1 : 0,
                                    ($suunta == -1) ? 0 : 2);
            $muutos = 6 + $rivi;
        }
    // yläsivun pyöritys
    } elseif ($sivu == "y") {
        if ($akseli == "x") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 1 : 0,
                                    ($suunta == -1) ? 2 : 0);
            $muutos = 3 + $rivi;
        } elseif ($akseli == "y") {
            $uusi = pyöritäkuutiota($kuutio, ($suunta == -1) ? 0 : 1,
                                    ($suunta == -1) ? 2 : 0);
            $muutos = 6 + $rivi;
        }
    }

    // muutetaan alkutilanteeseen pyörineet palat
    for ($i = 0; $i < 54; $i++) {
        if (in_array($i, $vanhat[$muutos])) {
            $kuutio[$i] = $uusi[$i];
        }
    }

    return $kuutio;
}

$kuutio = isset($_POST['kuutio']) ? $_POST['kuutio'] : sekoitakuutio();
$tila = isset($_POST['tila']) ? intval($_POST['tila']) : 0;
$ak = $_POST['ak'];

// kuution sekoitus
if (isset($_POST['sekoita'])) {
    $kuutio = sekoitakuutio();
    $tila = 0;
}

// kuvasta on napsautettu
if (isset($_POST['kuva_x'])) {
    // selvitetään painamiskohta
    $pkohta = laskekohta($_POST['kuva_x'], $_POST['kuva_y']);
    // tila 0 = tavallinen
    if ($tila == 0) {
        // pannaan talteen aloituskohta
        if ($pkohta) {
            $ak = implode($pkohta, " ");
            $tila = 1;
        // pyöritetään koko kuutiota
        } else {
            $kuutio = pyöritäkuutiota($kuutio,
                                      intval($_POST['kuva_x'] / (200 / 2)),
                                      intval($_POST['kuva_y'] / (200 / 3)));
        }
    // tila 1 = pyörityksen aloituskohta valittu
    } elseif ($tila == 1) {
        // pyöritetään yhtä kuution riviä
        if ($pkohta) {
            $vkohta = explode(" ", $ak);
            if ($vkohta[0] == $pkohta[0]) {
                if ($vkohta[1] == $pkohta[1]) {
                    $kuutio = pyöritäriviä($kuutio, $pkohta[0], "x", $pkohta[1],
                                           merkki($pkohta[2] - $vkohta[2]));
                }
                if ($vkohta[2] == $pkohta[2]) {
                    $kuutio = pyöritäriviä($kuutio, $pkohta[0], "y", $pkohta[2],
                                           merkki($pkohta[1] - $vkohta[1]));
                }
            }
        }
        $tila = 0;
    }
}

// piirtoskriptin vaatima kuution osien järjestys
$kuvajärj = array(0, 11, 24, 1, 10, 21, 2, 9, 18,
                  3, 14, 25, 4, 13, 22, 5, 12, 19,
                  6, 17, 26, 7, 16, 23, 8, 15, 20);

foreach($kuvajärj as $k) {
    $kuvadata .= $kuutio[$k];
}

?>

<html>
   <head>
      <title>Rubikin kuutio PHP:llä</title>
   </head>
   <body>
      <form action="rubik_p.php" method="post">
         <table border width="500">
             <tr>
                <td colspan="2"><h1>Rubikin kuutio</h1></td>
             </tr>
             <tr valign="top">
                <td>
                   <input type="image" name="kuva" src="rubik_k.php?<?php echo $kuvadata; ?>"
                </td>
                <td>
                   <p>Voit pyörittää kuutiota hiirellä. Jos napsautat kuution ulkopuolelta,
                   koko kuutio pyörii valittuun suuntaan. Kuutiossa oleva rivi pyörii
                   napsauttamalla kahta riviin kuuluvaa palaa pyörityssuunnan mukaisesti.</p>
                <input type="submit" name="sekoita" value="Sekoita">
                </td>
             </tr>
         </table>
         <input type="hidden" name="kuutio" value="<?php echo $kuutio; ?>">
         <input type="hidden" name="tila" value="<?php echo $tila; ?>">
         <input type="hidden" name="ak" value="<?php echo $ak; ?>">
      </form>
   </body>
</html>

ZcMander [05.01.2005 17:28:09]

#

Hyvä, mutta aika outo tuo rivin liikuttaminen, mutta paremminhan tuota ei voi tehdä.

makeuu [05.01.2005 17:43:01]

#

Mielenkiintoinen.

Juice [05.01.2005 19:38:31]

#

Mielenkiintoinen, mutta käyttökelvoton hitaalla yhteydellä :/

Kyllä Antti osaa :)

Blaze [05.01.2005 20:17:38]

#

Joko mun silmät harittaa, tai tuon kuution perspektiivissä on jotain vialla :)

msdos464 [05.01.2005 21:59:09]

#

Ei ole oikein käyttökelpoinen tuollaisena... Menee into :)
Kädessä pyöritellessä on paljon helpompi

Antti Laaksonen [06.01.2005 00:50:41]

#

Kuution mittoja voi hienosäätää mm. $ps-muuttujan avulla. :)

Sekoitukseen on tainnut eksyä bugi: kaikkia värivaihtoehtoja ei saakaan takaisin alkutilanteeseen (korjaus tulossa).

FrozenFire [06.01.2005 12:26:03]

#

Hauska...

(mutta käyttö kelvoton hitailla aivoilla ;)

FooBat [06.01.2005 20:19:23]

#

Ehkäpä siihen on syynsä, että tuo on harvemmin toteutettu PHP:lla. Pahin puute tässä versiossa on rajoittunut näkökenttä ja varsin hitaasti tapahtuva kuution kääntely. Rivien kääntely on tehty melko intuitiivisesti, mutta undo toiminto olisi varsin tarpeellinen virhesiirtojen peruutukseen.

EDIT: Hmm. back-nappi taitaa kyllä toimia undo-toimintona, joten unohda koko juttu :)

Kokeilin tehdä tuota kuutiota, mutta en päässyt kuin puoleen väliin, jonka jälkeen tein pari siirtoa, jotka tulkittiin väärin ja onnistuin sekoittamaan kuution niin pahasti, että olisi pitänyt aloittaa alusta.

Graphic [07.01.2005 18:18:57]

#

lainaus:

Hauska...

(mutta käyttö kelvoton hitailla aivoilla ;)

Siispä täysin käyttökelvoton koodinpätkä. ;)

jcd3nton [12.01.2005 19:19:18]

#

Omituinen perspektiivi ärsyttää...

zigilii [14.01.2005 16:57:01]

#

Ratkaisin ton kuution ( ~ 45 min), mutta käytin oikean kuution mukana tulevia ohjeita :P
Pari kertaa tuo sekosi (tulkitsi väärin painalluksen), mutta onneksi back auttaa

Sweiz [19.07.2005 14:46:09]

#

On joo ihan hauska :D

Oldeboy [08.02.2006 22:04:19]

#

Nämä hiirellä pyöritettävät eivät toimi mac:koneiden kanssa... Kun nämä tunnistavat tämänkin kuvaksi ja aina kun siitä ottaa kiinni kone meinaa tallentaa kuvan rubiikinkuutiosta siirrettyyn paikkaan esim. työpöydälle :)

jlaire [05.10.2007 20:54:30]

#

Suunnilleen 5 minuuttia meni ensimmäisellä yrityksellä. Toteutuksen voisi tehdä paljon yksinkertaisemmin ja välillä klikkailut tulkitaan väärin, mutta ihan hauska esimerkki.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta