Strona główna » Po Godzinach » Gry » Magnetyczna Wieża Hanoi - kod źródłowy
 

Magnetyczna Wieża Hanoi - kod źródłowy

· Gra · Kod źródłowy ·

Wstęp

Aplikacja Magnetyczna Wieża Hanoi została napisana przy pomocy JavaScript, które zarządza wyświetlaną treścią zmieniając kod HTML oraz CSS. Użyte funkcje są zgodne z najnowszym standardem HTML5.

Umieszczenie na stronie

W celu umieszczenia na stronie gry wystarczy umieścić obiekt typu DIV, który będzie miał identyfikator _game.

  1. <div id="_game"></div>

Skrypt

Zmienne globalne

W celu poprawnego działania aplikacji potrzebnych jest kilka zmiennych globalnych, które mają za zadanie przechowywać informacje pomiędzy różnymi funkcjami. Ich znaczenie jest następujące:

  1. <script>
  2. var margin_rod = 20;
  3. var el_width = 200;
  4. var el_minwidth = 60;
  5. var n = 3;
  6. var paliki = [];
  7. var clicked = "";
  8. var lrod;

(2.) ustalenie marginesu pomiędzy kolejnymi palikami (łącznie z największym elementem). (3.) Maksymalna szerokość pojedynczego elementu. (4.) Najmniejsza szerokość elementu. Zapobiega to tworzeniu bardzo małych elementów. (5.) Domyślna ilość elementów na pierwszym paliku. (6.) Lista paliki będzie przechowywać jakie elementy są na którym paliku. Potrzebne są jeszcze zmienne, które przechowają (7.) kliknięty element oraz (8.) na którym paliku został kliknięty.

Rozpoczęcie gry

Zarówno uruchomienie nowej gry czy zrestartowanie obecnej polega na wywołaniu funkcji prepareGame() razem z argumentem, który będzie oznaczał ile krążków ma zostać położonych na pierwszy paliku.

  1. function prepareGame(new_n) {
  2.  var board = document.getElementById("_game");
  3.  board.innerHTML = "";
  4.  n = new_n;
  5.  paliki = [];
  6.  for(var i = 1; i <= 3; i++){
  7.   board.innerHTML += getRod(i);
  8.   paliki.push([]);
  9.  }
  10.  for(var i = n; i >= 1; i--){
  11.   board.innerHTML += getElement("el" + i, i, 360/n*i);
  12.   setElementPos(document.getElementById("el" + i), n - i, 1);
  13.   paliki[0].push("el" + i);
  14.  }
  15.  board.innerHTML += '<div id="_msg"></div>';
  16.  reportState("Oczekiwanie na pierwszy ruch");
  17. }

(2.) Znalezienie ekranu gry (3.) i jego wyczyszczenie. (4.) Zapamiętanie obecnej ilość krążków (do restartu gry). (5.) Wyczyść pozycję krążków. (6. - 9.) Utwórz trzy paliki. Następnie (10. - 14.) zaczynając od największego elementu: (11.) utwórz nowy obiekt, (12.) ustaw go i (13.) dodaj na koniec listy pierwszego palika. Elementy, które są na końcu danej listy palika są pierwszymi od góry. (15.) Utwórz obiekt pod komunikaty dla użytkownika i (16.) pokaż komunikat, że gra została przygotowana.

Do tworzenia palików służy funkcja getRod(). Tworzy ona n-ty palik, odpowiednio go ustawia na planszy i przypisuję funkcję putObject(), która odbiera zdarzenie odkładania elementu na wybrany palik.

  1. function getRod(n){
  2.  return '<div class="rod" id="rod'+n+'" style="left:'+((n - 0.5)*(margin_rod + el_width) - 20)+'px;bottom:0" onclick="putObject(this);"></div>';
  3. }

Podobną funkcją jest getElement(). Jej zadanie polega na utworzeniu elementu o określonej szerokości, ustaleniu identyfikatora, koloru oraz co ma się stać jak zostanie kliknięty. Funkcja nei ustala pozycji elementu na planszy, a jedynie jego cechy wygladu i zdarzenia.

  1. function getElement(id,num,col){
  2.  return '<div id="'+id+'" class="el" style="background:hsl('+col+',80%,50%);width:'+(el_minwidth + num*(el_width-el_minwidth)/n)+'px" onclick="setObject(this);"><div class="t"></div><div class="b"></div></div>';
  3. }

Do ustawiania elementu na stronie służy funkcja setElementPos(). Rozwiązanie to jest o tyle lepsze, że elementy są przestawiane w trakcie rozgrywki, a funkcja jest wykorzystana podczas tworzenia rozgrywki jak i jej samej.

  1. function setElementPos(obj, num, rod){
  2.  obj.style.left = ((rod - 0.5)*(margin_rod + el_width) - obj.style.width.replace("px", "")/2) + "px";
  3.  obj.style.bottom = (40*num) + "px";
  4. }

Ostatnia funkcja podczas przygotowania gry - reportState() wyświetla komunikaty dla gracza (o niepoprawnym ruchu czy, że gra została rozpoczęta.) Jest to zaledwie jedna linijka, która polega na znalezieniu odpowiedniego obiektu i zmianie jego zawartości:

  1. function reportState(msg){
  2.  document.getElementById("_msg").innerHTML = msg;
  3. }

Rozgrywka

Po kliknięciu dowolnego elementu wywoływana jest funkcja setObject():

  1. function setObject(el) {
  2.  if(clicked!="")
  3.   clicked.className = clicked.className.replace("el elc", "el");
  4.  if(el){
  5.   for(var i = 0; i < 3; i++){
  6.    reportState("Element podniesiony");
  7.    if(el.id == paliki[i][paliki[i].length - 1]){
  8.     clicked = el;
  9.     lrod = i;
  10.     el.className = el.className.replace("el", "el elc");
  11.     return;
  12.    }
  13.   }
  14.   reportState("Element nie może zostać podniesiony");
  15.  }
  16.  clicked = "";
  17. }

(2.) Jeśli jest jakiś kliknięty obiekt to (3.) go odznacz. (4.) Jeśli kliknięty jest rzeczywiście obiektem to (5.) zacznij szukać palika na którym jest on element na wierzchu. (6.) Zgłoś status o poniesieniu elementu. (7.) Jeśli i-ty palik ma kliknięty element el na wierzchu to: (8.) zapamiętaj go (9.) oraz palik na którym się znajduje. (10.) Dodaj klasę do obiektu, aby użytkownik zobaczył, że kliknął element. (11.) Zakończ wywoływanie funkcji. (12.) Jednak jeśli funkcja nie zostanie przerwana to: (14.) zgłoś, że element nie może być podniesiony i (16.) przed zakończeniem funkcji ustal, że żaden element nie jest kliknięty.

Po podniesieniu elementu gracz klika na wybrany palik. Do obsługi tego zdarzenie służy funkcja putObject():

  1. function putObject(obj) {
  2.  var rod = obj.id.replace("rod", "");
  3.  if (clicked == "") {
  4.   reportState('Nie wybrano elementu');
  5.   return;
  6.  }
  7.  if((lastElement(rod - 1) != undefined) && (getLastElement(rod - 1).className.indexOf("flip") == clicked.className.indexOf("flip"))) {
  8.   reportState('Nie można opuścić tutaj elementu, niezgodność biegunów');
  9.   return;
  10.  }
  11.  if((lastElement(rod - 1) == undefined || lastElement(rod - 1).replace("e1_","").replace("e2_","") >= clicked.id.replace("e1_","").replace("e2_",""))){
  12.   reportState("Element przełożony");
  13.   paliki[rod - 1].push(paliki[lrod].pop());
  14.   setElementPos(clicked, paliki[rod - 1].length - 1, rod);
  15.   if(clicked.className.indexOf("flip") > -1){
  16.    clicked.className = clicked.className.replace("flip el", "el");
  17.   } else {
  18.    clicked.className = clicked.className.replace("el", "flip el");
  19.   }
  20.   setObject("");
  21.   if(checkWin()){
  22.    reportState('Zagadka rozwiązana, <a href="javascript:void()" class="cU" onclick="prepareGame(n);">zrestartuj grę</a>');
  23.   }
  24.  } else {
  25.   reportState('Nie można kłaść większego krążka na mniejszym');
  26.  }
  27. }

(2.) Pobierz numer klikniętego palika. (3.) Sprawdź czy został wybrany jakikolwiek element. Jeśli nie to (4.) pokaż odpowiednią wiadomość i (5.) zakończ działanie funkcji. (7.) Następnie sprawdź czy po obróceniu krążka bieguny będą odpowiednio ustawione. Jeśli nie to: (8.) pokaż odpowiedni komunikat i (9.) zakończ działania funkcji.

(11.) Ostatnim warunkiem do sprawdzenia pozostaje sprawdzenie czy przekładany krążek nie jest większy od już położonego. (12.) Pokaż komunikat i (13.) przełóż element na liście. (14.) Przestaw element w widoczny sposób dla gracza. Dodatkowo (15. - 19.) obróć element poprzez dodanie / usunięcie klasy flip. (20.) Wyczyść wybrany obiekt i (21.) sprawdź warunek zwycięstwa. Jeśli gra została zakończona sukcesem to (22.) pokaż komunikat. Jednak jeśli położony jest większy krążek na mniejszy to (25.) pokaż odpowiedni komunikat.

Warunek zwycięstwa jest sprawdzany przy pomocy funkcji checkWin(). Sprawdza ona czy każdy kolejny element na liście środkowego palika ma coraz mniejsze numery elementów:

  1. function checkWin(){
  2.  for(var i = n; i >= 1; i--){
  3.   if(paliki[2][n - i] != "el" + i)
  4.    return false;
  5.  }
  6.  return true;
  7. }

Niewymieniona jeszcze funkcja lastElement() ułatwia wybranie ostatniego elementu z dowolnego palika:

  1. function lastElement(rod) {
  2.  return paliki[rod][paliki[rod].length - 1];
  3. }

Z kolei funkcja getLastElement() korzysta z lastElement() i zwraca konkretny obiekt umieszczony na stronie, a nie tylko jego identyfikator.

  1. function getLastElement(rod) {
  2.  return document.getElementById(lastElement(rod));
  3. }

Rozpoczęcie gry

Grę można rozpocząć przy pomocy funkcji prepareGame(). Na przykład w celu zrestartowania gry / uruchomienia z domyślną konfiguracją należy wpisać:

  1. prepareGame(n);

Styl rozgrywki

Elementy gry są częściowo ustalana poprzez kod CSS. Styl ekranu gry:

  1. #_game {
  2.  width: 700px;
  3.  height: 400px;
  4.  background: #82b1ff;
  5.  overflow: hidden;
  6.  position: relative;
  7.  margin: 0 auto;
  8. }

Styl pola komunikatów:

  1. #_msg {
  2.  width: 700px;
  3.  top: 0;
  4.  padding: 4px;
  5.  background: #cfd8dc;
  6. }

Styl palika:

  1. .rod {
  2.  width: 40px;
  3.  background: #7F3300;
  4.  border: 1px solid gray;
  5.  position: absolute;
  6.  height: 300px;
  7.  border-radius: 6px;
  8. }

Styl elementu:

  1. .el {
  2.  position: absolute;
  3.  height: 40px;
  4.  border: 1px solid gray;
  5.  border-radius: 6px;
  6.  line-height: 40px;
  7.  font-size: 30px;
  8.  font-weight: 700;
  9.  text-align: center;
  10.  text-shadow: 1px 1px 2px white;
  11. }

Styl wybranego elementu:

  1. .elc {
  2.  opacity: 0.5;
  3. }

Dodatkowo istnieje potrzeba na zadeklarowanie dwóch podklas klasy .el, które będą odpowiednio kolorować podobiekty obiektu div reprezentującego krążek:

  1. .el>.t,.el>.b{
  2.  width:100%;
  3.  height:50%
  4. }
  5. .el>.t{ background:red }
  6. .el>.b{ background:blue }

Oddzielnie zapisana jest klasa .flip, która pozwala na zasymulowanie obrotu krążka:

  1. .flip{ transform:rotate(180deg) }