9.5. Algorytmy
W tym scenariuszu spróbujemy pokazać w Minecrafcie Pi algorytm symulujący ruchy Browna oraz algorytm stosujący metodę Monte Carlo do wyliczenia przybliżonej wartości liczby Pi.
9.5.1. Ruchy Browna
Za pomocą wybranego edytora utwórz pusty plik, umieść w nim podany niżej kod i zapisz
w katalogu mcpi-sim pod nazwą mcpi-rbrowna.py:
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import os
5import numpy as np # import biblioteki do obliczeń naukowych
6import matplotlib.pyplot as plt # import biblioteki do tworzenia wykresow
7from random import randint
8from time import sleep
9import mcpi.minecraft as minecraft # import modułu minecraft
10import mcpi.block as block # import modułu block
11
12os.environ["USERNAME"] = "Steve" # wpisz dowolną nazwę użytkownika
13os.environ["COMPUTERNAME"] = "mykomp" # wpisz dowolną nazwę komputera
14
15mc = minecraft.Minecraft.create("192.168.1.10") # połączenie z serwerem
16
17
18def plac(x, y, z, roz=10, gracz=False):
19 """
20 Funkcja tworzy podłoże i wypełnia sześcienny obszar od podanej pozycji,
21 opcjonalnie umieszcza gracza w środku.
22 Parametry: x, y, z - współrzędne pozycji początkowej,
23 roz - rozmiar wypełnianej przestrzeni,
24 gracz - czy umieścić gracza w środku
25 Wymaga: globalnych obiektów mc i block.
26 """
27
28 podloga = block.SAND
29 wypelniacz = block.AIR
30
31 # podloga i czyszczenie
32 mc.setBlocks(x, y - 1, z, x + roz, y - 1, z + roz, podloga)
33 mc.setBlocks(x, y, z, x + roz, y + roz, z + roz, wypelniacz)
34 # umieść gracza w środku
35 if gracz:
36 mc.player.setPos(x + roz / 2, y + roz / 2, z + roz / 2)
37
38
39def wykres(x, y, tytul="Wykres funkcji", *extra):
40 """
41 Funkcja wizualizuje wykres funkcji, której argumenty zawiera lista x
42 a wartości lista y i ew. dodatkowe listy w parametrze *extra
43 """
44 if len(extra):
45 plt.plot(x, y, extra[0], extra[1]) # dwa wykresy na raz
46 else:
47 plt.plot(x, y, "o:", color="blue", linewidth="3", alpha=0.8)
48 plt.title(tytul)
49 plt.grid(True)
50 plt.show()
51
52
53def rysuj(x, y, z, blok=block.IRON_BLOCK):
54 """
55 Funkcja wizualizuje wykres funkcji, umieszczając bloki w pionie/poziomie
56 w punktach wyznaczonych przez pary elementów list x, y lub x, z
57 """
58 czylista = True if len(y) > 1 else False
59 for i in range(len(x)):
60 if czylista:
61 print(x[i], y[i])
62 mc.setBlock(x[i], y[i], z[0], blok)
63 else:
64 print(x[i], z[i])
65 mc.setBlock(x[i], y[0], z[i], blok)
66
67
68def ruchyBrowna():
69
70 n = int(raw_input("Ile ruchów? "))
71 r = int(raw_input("Krok przesunięcia? "))
72
73 x = y = 0
74 lx = [0] # lista odciętych
75 ly = [0] # lista rzędnych
76
77 for i in range(0, n):
78 # losujemy kąt i zamieniamy na radiany
79 rad = float(randint(0, 360)) * np.pi / 180
80 x = x + r * np.cos(rad) # wylicz współrzędną x
81 y = y + r * np.sin(rad) # wylicz współrzędną y
82 x = int(round(x, 2)) # zaokrągl
83 y = int(round(y, 2)) # zaokrągl
84 print(x, y)
85 lx.append(x)
86 ly.append(y)
87
88 # oblicz wektor końcowego przesunięcia
89 s = np.fabs(np.sqrt(x**2 + y**2))
90 print "Wektor przesunięcia: {:.2f}".format(s)
91
92 wykres(lx, ly, "Ruchy Browna")
93 rysuj(lx, [1], ly, block.WOOL)
94
95
96def main():
97 mc.postToChat("Ruchy Browna") # wysłanie komunikatu do mc
98 plac(-80, -20, -80, 160)
99 plac(-80, 0, -80, 160)
100 ruchyBrowna()
101 return 0
102
103
104if __name__ == '__main__':
105 main()
Większość kodu powinna być już zrozumiała. Importy bibliotek, nawiązywanie połączenia
z serwerem MC Pi, funkcje plac(), wykres() i rysuj() omówione zostały w poprzednim
scenariuszu Funkcje w mcpi.
W funkcji ruchyBrowna() na początku pobieramy od użytkownika ilość ruchów cząsteczki
do wygenerowania oraz ich długość, co ma znaczenie podczas ich odwzorowywania w świecie MC Pi.
Następnie w pętli:
losujemy kąt wskazujący kierunek ruchu cząsteczki,
wyliczamy współrzędne kolejnego punktu korzystając z funkcji cos() i sin() (np.
x = x + r * np.cos(rad)),zaokrąglamy wyniki do 2 miejsc po przecinku (np.
x = int(round(x, 2))) i drukujemy,na koniec dodajemy obliczone współrzędne do list odciętych i rzędnych (np.
lx.append(x)).
Po wyjściu z pętli obliczamy długość wektora przesunięcia, korzystając z twierdzenia Pitagorasa,
i drukujemy wynik z dokładnością do dwóch miejsc po przecinku (wyrażenie formatujące: {:.2f}).
Po tych operacjach pozostaje wykreślenie ruchu cząsteczki w matplotlib i wyznaczenie go w Minecrafcie.
Wskazówka
Przed uruchomieniem wizualizacji warto ustawić Steve’a w tryb lotu (dwukrotne naciśnięcie spacji).
9.5.1.1. (Nie)powtarzalność
Kilkukrotne uruchomienie dotychczasowego kodu pokazuje, że za każdym razem generowany jest inny tor ruchu cząsteczki. Z jednej strony to dobrze, bo to potwierdza przypadkowość symulowanych ruchów, z drugiej strony przydatna byłaby możliwość zapamiętania wyjątkowo malowniczych sekwencji.
Zmienimy więc funkcję ruchyBrowna() tak, aby zapisywała i ewentualnie
odczytywała wygenerowany i zapisany ruch cząsteczki. Musimy też dodać dwie funkcje narzędziowe
zapisujące i czytające dane.
68def ruchyBrowna(dane=[]):
69
70 if len(dane):
71 lx, ly = dane # rozpakowanie listy
72 x = lx[-1] # ostatni element lx
73 y = ly[-1] # ostatni element ly
74 else:
75 n = int(raw_input("Ile ruchów? "))
76 r = int(raw_input("Krok przesunięcia? "))
77
78 x = y = 0
79 lx = [0] # lista odciętych
80 ly = [0] # lista rzędnych
81
82 for i in range(0, n):
83 # losujemy kąt i zamieniamy na radiany
84 rad = float(randint(0, 360)) * np.pi / 180
85 x = x + r * np.cos(rad) # wylicz współrzędną x
86 y = y + r * np.sin(rad) # wylicz współrzędną y
87 x = int(round(x, 2)) # zaokrągl
88 y = int(round(y, 2)) # zaokrągl
89 print(x, y)
90 lx.append(x)
91 ly.append(y)
92
93 # oblicz wektor końcowego przesunięcia
94 s = np.fabs(np.sqrt(x**2 + y**2))
95 print "Wektor przesunięcia: {:.2f}".format(s)
96
97 wykres(lx, ly, "Ruchy Browna")
98 rysuj(lx, [1], ly, block.WOOL)
99 if not len(dane):
100 zapisz_dane((lx, ly))
101
102
103def zapisz_dane(dane):
104 """Funkcja zapisuje dane w formacie json w pliku"""
105 import json
106 plik = open('rbrowna.log', 'w')
107 json.dump(dane, plik)
108 plik.close()
109
110
111def czytaj_dane():
112 """Funkcja odczytuje dane w formacie json z pliku"""
113 import json
114 dane = []
115 nazwapliku = raw_input("Podaj nazwę pliku z danymi lub naciśnij ENTER: ")
116 if os.path.isfile(nazwapliku):
117 with open(nazwapliku, "r") as plik:
118 dane = json.load(plik)
119 else:
120 print "Podany plik nie istnieje!"
121 return dane
122
123
124def main():
125 mc.postToChat("Ruchy Browna") # wysłanie komunikatu do mc
126 plac(-80, -20, -80, 160)
127 plac(-80, 0, -80, 160)
128 ruchyBrowna(czytaj_dane())
129 return 0
Z powyższego kodu wynika, że jeżeli funkcja ruchyBrowna() otrzyma niepustą listę danych
(if len(dane):), wczyta z niej dane współrzędnych x i y. W przeciwnym wypadku
generowane będą nowe, które zostaną zapisane: zapisz_dane((lx, ly)).
Funkcja zapisz_dane(), pobiera tuplę zawierającą listę współrzędnych x i y,
otwiera plik o podanej nazwie do zapisu (open('rbrowna.log', 'w')) i zapisuje
w nim dane w formacie json.
Funkcja czytaj_dane() prosi o podanie nazwy pliku z danymi, jeśli istnieje,
zwraca dane zapisane w formacie json, które w funkcji ruchyBrowna()
rozpakowywane są jako listy wartości x i y: lx, ly = dane.
Jeżeli podany plik z danymi nie istnieje, zwracana jest pusta lista,
a w funkcji ruchyBrowna() generowane są nowe dane.
W funkcji głównej zmieniamy wywołanie funkcji na ruchyBrowna(czytaj_dane())
i testujemy zmieniony kod. Za pierwszym razem wciskamy Enter, generujemy
i zapisujemy dane, za drugim razem podajemy nazwę pliku rbrowna.log.
9.5.1.2. Ruch cząsteczki
Do tej pory ruch cząsteczki wizualizowane był jako pojedyncze punkty.
Możemy jednak pokazać pokonaną trasę liniowo, używając omawianej już
biblioteki minecraftstaff. Pod funkcją rysuj() umieszczamy
następującą funkcję:
68def rysuj_linie(x, y, z, blok=block.IRON_BLOCK):
69 """
70 Funkcja wizualizuje wykres funkcji, umieszczając bloki w pionie/poziomie
71 w punktach wyznaczonych przez pary elementów list x, y lub x, z
72 przy użyciu metody drawLine()
73 """
74 import local.minecraftstuff as mcstuff
75 mcfig = mcstuff.MinecraftDrawing(mc)
76 czylista = True if len(y) > 1 else False
77 for i in range(len(x) - 1):
78 x1 = int(x[i])
79 x2 = int(x[i + 1])
80 if czylista:
81 y1 = int(y[i])
82 y2 = int(y[i + 1])
83 mc.setBlock(x2, y2, z[0], block.GRASS)
84 mc.setBlock(x1, y1, z[0], block.GRASS)
85 mcfig.drawLine(x1, y1, z[0], x2, y2, z[0], blok)
86 mc.setBlock(x2, y2, z[0], block.GRASS)
87 mc.setBlock(x1, y1, z[0], block.GRASS)
88 print (x1, y1, z[0], x2, y2, z[0])
89 else:
90 z1 = int(z[i])
91 z2 = int(z[i + 1])
92 mc.setBlock(x1, y[0], z1, block.GRASS)
93 mc.setBlock(x2, y[0], z2, block.GRASS)
94 mcfig.drawLine(x1, y[0], z1, x2, y[0], z2, blok)
95 mc.setBlock(x1, y[0], z1, block.GRASS)
96 mc.setBlock(x2, y[0], z2, block.GRASS)
97 print (x1, y[0], z1, x2, y[0], z2)
98 sleep(1) # przerwa na reklamę :-)
99 mc.setBlock(0, 1, 0, block.OBSIDIAN)
100 if czylista:
101 mc.setBlock(x2, y2, z[0], block.OBSIDIAN)
102 else:
103 mc.setBlock(x2, y[0], z2, block.OBSIDIAN)
Jak widać, jest to zmodyfikowana funkcja, której użyliśmy po raz pierwszy w scenariuszu
Funkcje. Zmiany dotyczą dodatkowych instrukcji
typu mc.setBlock(x2, y2, z[0], block.GRASS), których zadaniem jest zaznaczenie
innymi blokami wylosowanych punktów reprezentujących ruch cząsteczki.
Instrukcja sleep(1) wstrzymując budowanie na 1 sekundę wywołuje wrażenie
animacji i pozwala śledzić na bieżąco budowany tor.
Końcowe instrukcje służą zaznaczeniu początku i końca ruchu blokami obsydianu.
Na koniec trzeba w funkcji ruchyBrowna() zmienić wywołanie rysuj() na
rysuj_linie().
Eksperymenty
Uruchamiamy kod i eksperymentujemy. Dla 100 ruchów z krokiem przesunięcia 5 możemy uzyskać np. takie rezultaty:
Nic nie stoina przeszkodzie, żeby cząsteczka „ruszała się” w pionie nad i… pod wodą:
9.5.2. Liczba Pi
Mamy koło o promieniu r, którego środek umieszczamy w początku układu współrzędnych (0, 0).
Na kole opisany jest kwadrat o boku 2r. Spróbujmy to zbudować w MC Pi. W pliku mcpi-lpi.py
umieszczamy kod:
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4import os
5import random
6from time import sleep
7import mcpi.minecraft as minecraft # import modułu minecraft
8import mcpi.block as block # import modułu block
9import local.minecraftstuff as mcstuff
10
11os.environ["USERNAME"] = "Steve" # nazwa użytkownika
12os.environ["COMPUTERNAME"] = "mykomp" # nazwa komputera
13
14mc = minecraft.Minecraft.create("192.168.1.10") # połączenie z serwerem
15
16
17def plac(x, y, z, roz=10, gracz=False):
18 """Funkcja wypełnia sześcienny obszar od podanej pozycji
19 powietrzem i opcjonalnie umieszcza gracza w środku.
20 Parametry: x, y, z - współrzędne pozycji początkowej,
21 roz - rozmiar wypełnianej przestrzeni,
22 gracz - czy umieścić gracza w środku
23 Wymaga: globalnych obiektów mc i block.
24 """
25
26 podloga = block.STONE
27 wypelniacz = block.AIR
28
29 # kamienna podłoże
30 mc.setBlocks(x, y - 1, z, x + roz, y - 1, z + roz, podloga)
31 # czyszczenie
32 mc.setBlocks(x, y, z, x + roz, y + roz, z + roz, wypelniacz)
33 # umieść gracza w środku
34 if gracz:
35 mc.player.setPos(x + roz / 2, y + roz / 2, z + roz / 2)
36
37
38def model(promien, x, y, z):
39 """
40 Fukcja buduje obrys kwadratu, którego środek to punkt x, y, z
41 oraz koło wpisane w ten kwadrat
42 """
43
44 mcfig = mcstuff.MinecraftDrawing(mc)
45 obrys = block.SANDSTONE
46 wypelniacz = block.AIR
47
48 mc.setBlocks(x - promien, y, z - promien, x +
49 promien, y, z + promien, obrys)
50 mc.setBlocks(x - promien + 1, y, z - promien + 1, x +
51 promien - 1, y, z + promien - 1, wypelniacz)
52 mcfig.drawHorizontalCircle(0, 0, 0, promien, block.GRASS)
53
54
55def liczbaPi():
56 r = float(raw_input("Podaj promień koła: "))
57 model(r, 0, 0, 0)
58
59
60def main():
61 mc.postToChat("LiczbaPi") # wysłanie komunikatu do mc
62 plac(-50, 0, -50, 100)
63 mc.player.setPos(20, 20, 0)
64 liczbaPi()
65 return 0
66
67
68if __name__ == '__main__':
69 main()
Funkcja model() działa podobnie do funkcji plac(), czyli na początku
budujemy wokół środka układu współrzędnych płytę z bloku, który będzie zarysem kwadratu.
Później budujemy drugą płytę o blok mniejszą z powietrza. Na koniec rysujemy koło.
9.5.2.1. Deszcz punktów
Teraz wyobraźmy sobie, że pada deszcz. Część kropel upada w obrębie kwadratu,
ich ilość oznaczymy zmienną ileKw, a część również w obrębie koła – oznaczymy
je zmienną ileKo. Ponieważ znamy promień koła, możemy ułożyć proporcję, zakładając, że
stosunek pola koła do pola kwadratu równy będzie stosunkowi kropel w kole do kropel
w kwadracie:

Z prostego przekształcenia tej równości możemy wyznaczyć liczbę Pi:

Uzupełniamy więc kod funkcji liczbaPi():
55def liczbaPi():
56 r = float(raw_input("Podaj promień koła: "))
57 model(r, 0, 0, 0)
58
59 # pobieramy ilość punktów w kwadracie
60 ileKw = int(raw_input("Podaj ilość losowanych punktów: "))
61 ileKo = 0 # ilość punktów w kole
62
63 blok = block.SAND
64 for i in range(ileKw):
65 x = round(random.uniform(-r, r))
66 y = round(random.uniform(-r, r))
67 print x, y
68 if abs(x)**2 + abs(y)**2 <= r**2:
69 ileKo += 1
70 # umieść blok w MC Pi
71 mc.setBlock(x, 10, y, blok)
72
73 mc.postToChat("W kole = " + str(ileKo) + " W Kwadracie = " + str(ileKw))
74 pi = 4 * ileKo / float(ileKw)
75 mc.postToChat("Pi w przyblizeniu: {:.10f}".format(pi))
76
Jak widać w nowym kodzie, na początku pobieramy od użytkownika ilość „kropel” deszczu,
czyli punktów do wylosowania. Następnie w pętli losujemy ich współrzędne
w przedziale <-r;r> w instrukcji typu: x = round(random.uniform(-r, r), 10).
Funkcja uniform() zwraca wartości zmiennoprzecinkowe, które zaokrąglamy
do 10 miejsca po przecinku.
Korzystając z twierdzenia Pitagorasa układamy warunek pozwalający sprawdzić,
które punkty „wpadły” do koła: if abs(x)**2 + abs(y)**2 <= r**2: – i zliczamy je.
Instrukcja mc.setBlock(x, 10, y, blok) rysuje punkty w MC Pi za pomocą
bloków piasku (SAND), dzięki czemu uzyskujemy efekt spadania.
Wyliczenie wartości Pi i wydrukowanie jej jest prostą formalnością.
Uruchomienie powyższego kodu dla promienia 30 i 1000 punktów dało następujący efekt:
Jak widać, niektóre punkty po zaokrągleniu ich współrzędnych w MC Pi nakładają się na siebie.
9.5.2.2. Podkolorowanie
Punkty wpadające do koła mogłyby wyglądać inaczej niż poza nim. Można by to
osiągnąć przez ustawienie różnych typów bloków w pętli for, ale tylko
blok piaskowy daje efekt spadania. Zrobimy więc inaczej. Zmieniamy funkcję liczbaPi():
55def liczbaPi():
56 r = float(raw_input("Podaj promień koła: "))
57 model(r, 0, 0, 0)
58
59 # pobieramy ilość punktów w kwadracie
60 ileKw = int(raw_input("Podaj ilość losowanych punktów: "))
61 ileKo = 0 # ilość punktów w kole
62 wKwadrat = [] # pomocnicza lista punktów w kwadracie
63 wKolo = [] # pomocnicza lista punktów w kole
64
65 blok = block.SAND
66 for i in range(ileKw):
67 x = round(random.uniform(-r, r))
68 y = round(random.uniform(-r, r))
69 wKwadrat.append((x, y))
70 print x, y
71 if abs(x)**2 + abs(y)**2 <= r**2:
72 ileKo += 1
73 wKolo.append((x, y))
74
75 mc.setBlock(x, 10, y, blok)
76
77 sleep(5)
78 for pkt in set(wKwadrat) - set(wKolo):
79 x, y = pkt
80 mc.setBlock(x, i, y, block.OBSIDIAN)
81 for i in range(1, 3):
82 print x, i, y
83 if mc.getBlock(x, i, y) == 12:
84 mc.setBlock(x, i, y, block.OBSIDIAN)
85
86 mc.postToChat("W kole = " + str(ileKo) + " W Kwadracie = " + str(ileKw))
87 pi = 4 * ileKo / float(ileKw)
88 mc.postToChat("Pi w przyblizeniu: {:.10f}".format(pi))
Deklarujemy dwie pomocnicze listy, do których zapisujemy w pętli współrzędne
punktów należących do kwadratu i koła, np. wKwadrat.append((x, y)).
Następnie wstrzymujemy wykonanie kodu na 5 sekund, aby bloki piasku
zdążyły opaść. W wyrażeniu set(wKwadrat) - set(wKolo) każda lista zostaje
przekształcona na zbiór, a następnie zostaje obliczona ich różnica.
W efekcie otrzymujemy współrzędne punktów należących do kwadratu, ale nie do koła.
Ponieważ niektóre bloki piasku układają się jeden na drugim, wychwytujemy je w pętli
wewnętrznej if mc.getBlock(x, i, y) == 12: – i zmieniamy na obsydian.
9.5.2.3. Trzeci wymiar
Siła MC Pi tkwi w 3 wymiarze. Możemy bez większych problemów go wykorzystać. Na początku warto zauważyć, że w algorytmie wyliczania wartości liczby Pi nic się nie zmieni. Stosunek pola koła do pola kwadratu zastępujemy bowiem stosunkiem objętości walca, którego podstawa ma promień r, do objętości sześcianu o boku 2r. Otrzymamy zatem:

Po przekształceniu skończymy na takim samym jak wcześniej wzorze, czyli:

Aby to wykreślić, zmienimy funkcje model(), liczbaPi() i main(). Sugerujemy, żeby
dotychczasowy plik zapisać pod inną nazwą, np. mcpi-lpi3D.py, i wprowadzić następujące zmiany:
38def model(r, x, y, z, klatka=False):
39 """
40 Fukcja buduje obrys kwadratu, którego środek to punkt x, y, z
41 oraz koło wpisane w ten kwadrat
42 """
43
44 mcfig = mcstuff.MinecraftDrawing(mc)
45 obrys = block.OBSIDIAN
46 wypelniacz = block.AIR
47
48 mc.setBlocks(x - r - 10, y - r, z - r - 10, x +
49 r + 10, y + r, z + r + 10, wypelniacz)
50 mcfig.drawLine(x + r, y + r, z + r, x - r, y + r, z + r, obrys)
51 mcfig.drawLine(x - r, y + r, z + r, x - r, y + r, z - r, obrys)
52 mcfig.drawLine(x - r, y + r, z - r, x + r, y + r, z - r, obrys)
53 mcfig.drawLine(x + r, y + r, z - r, x + r, y + r, z + r, obrys)
54
55 mcfig.drawLine(x + r, y - r, z + r, x - r, y - r, z + r, obrys)
56 mcfig.drawLine(x - r, y - r, z + r, x - r, y - r, z - r, obrys)
57 mcfig.drawLine(x - r, y - r, z - r, x + r, y - r, z - r, obrys)
58 mcfig.drawLine(x + r, y - r, z - r, x + r, y - r, z + r, obrys)
59
60 mcfig.drawLine(x + r, y + r, z + r, x + r, y - r, z + r, obrys)
61 mcfig.drawLine(x - r, y + r, z + r, x - r, y - r, z + r, obrys)
62 mcfig.drawLine(x - r, y + r, z - r, x - r, y - r, z - r, obrys)
63 mcfig.drawLine(x + r, y + r, z - r, x + r, y - r, z - r, obrys)
64
65 mc.player.setPos(x + r, y + r + 1, z + r)
66
67 if klatka:
68 mc.setBlocks(x - r, y - r, z - r, x + r, y + r, z + r, block.GLASS)
69 mc.setBlocks(x - r + 1, y - r + 1, z - r + 1, x +
70 r - 1, y + r - 1, z + r - 1, wypelniacz)
71 mc.player.setPos(0, 0, 0)
72
73 for i in range(-r, r + 1, 5):
74 mcfig.drawHorizontalCircle(0, i, 0, r, block.GRASS)
75
76
77def liczbaPi(klatka=False):
78 r = int(raw_input("Podaj promień koła: "))
79 model(r, 0, 0, 0, klatka)
80
81 # pobieramy ilość punktów w kwadracie
82 ileKw = int(raw_input("Podaj ilość losowanych punktów: "))
83 ileKo = 0 # ilość punktów w kole
84 wKwadrat = [] # pomocnicza lista punktów w kwadracie
85 wKolo = [] # pomocnicza lista punktów w kole
86
87 for i in range(ileKw):
88 blok = block.OBSIDIAN
89 x = round(random.uniform(-r, r))
90 y = round(random.uniform(-r, r))
91 z = round(random.uniform(-r, r))
92 wKwadrat.append((x, y, z))
93 print x, y, z
94 if abs(x)**2 + abs(z)**2 <= r**2:
95 blok = block.DIAMOND_BLOCK
96 ileKo += 1
97 wKolo.append((x, y, z))
98
99 mc.setBlock(x, y, z, blok)
100
101 mc.postToChat("W kole = " + str(ileKo) + " W Kwadracie = " + str(ileKw))
102 pi = 4 * ileKo / float(ileKw)
103 mc.postToChat("Pi w przyblizeniu: {:.10f}".format(pi))
104 mc.postToChat("Stan na kamieniu!")
105
106 while True:
107 poz = mc.player.getPos()
108 x, y, z = poz
109 if mc.getBlock(x, y - 1, z) == block.STONE.id:
110 for pkt in wKolo:
111 x, y, z = pkt
112 mc.setBlock(x, y, z, block.SAND)
113 sleep(3)
114 mc.player.setPos(0, r - 1, 0)
115 break
116
117
118def main():
119 mc.postToChat("LiczbaPi") # wysłanie komunikatu do mc
120 plac(-50, 0, -50, 100)
121 liczbaPi(False)
122 return 0
Zadaniem funkcji model() jest stworzenie przestrzeni dla obrysu sześcianu i jego szkieletu.
Opcjonalnie, jeżeli przekażemy do funkcji parametr klatka równy True,
ściany mogą zostać wypełnione szkłem. Walec wizualizujemy w pętli for
rysując kilka okręgów blokami trawy.
W funkcji liczbaPi() najważniejszą zmianą jest dodanie trzeciej zmiennej.
Wartości wszystkich trzech współrzędnych losowane są w takim samym zakresie,
ponieważ za środek całego układu przyjmujemy początek układu współrzędnych.
Ważna zmiana zachodzi w funkcji warunkowej: if abs(x)**2 + abs(z)**2 <= r**2:.
Do sprawdzenia, czy punkt należy do koła wykorzystujemy zmienne x i z,
uwzględniając fakt, że w MC Pi wyznaczają one położenie w poziomie.
Bloki należące do sześcianu rysujemy za pomocą obsydianu, te w walcu – za pomocą diamentów.
Na końcu funkcji dodajemy nieskończoną pętlę (while True:), której zadaniem jest
sprawdzanie, na jakim bloku znajduje się gracz: if mc.getBlock(x, y - 1, z) == block.STONE.id:.
Jeżeli stanie on na kamieniu, wszystkie bloki należące do walca zamieniamy
w pętli for pkt in wKolo: w piasek, a gracza teleportujemy do środka sześcianu.
Dla promienia o wielkości 20 i 1000 bloków uzyskać można poniższe budowle:
Pozostaje eksperymentować z rozmiarami, typami bloków czy parametrem klatka
określanym w wywołaniu funkcji liczbaPi() w funkcji głównej.
Źródła:
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
- Utworzony:
2026-05-30 o 19:12 w Sphinx 7.3.7
- Autorzy: