Drzwi otwierane fotokomórką.

Chciałbym nauczyć dzieciaki podstaw programowania. Długo zastanawiałem się jak to zrobić i po przemyśleniach postanowiłem spróbować pythonowego mcpi, bo maluchy od kilku lat budują co popadnie w Minecraft na Androidzie. Uruchomiłem spigot, zainstalowałem RaspberryJuce i Geyser… I sam wsiąkłem na kilka wieczorów ;).

from mcpi import minecraft
import mcpi.block as block
import minecraftstuff
import time
from threading import Thread
import sys
from pathlib import Path

mc = minecraft.Minecraft.create()

if not (Path(".house_coordinates.txt").is_file()):
    print("Zbuduj najpierw dom za pomocą house.py")
    sys.exit()
else:
    f = open(".house_coordinates.txt", "r")
    x,y,z = f.readline().split(',')
    f.close()
    housePos = minecraft.Vec3(int(x),int(y),int(z))

blocksPos = [ (0,0,0), (-1,0,0), (-2,0,0), (0,1,0), (-1,1,0),
              (-2,1,0), (0,2,0), (-1,2,0), (-2,2,0), (0,3,0),
              (-1,3,0), (-2,3,0) ]

doorsPos = housePos + minecraft.Vec3(6,0,0)
doorsBlocks = [minecraftstuff.ShapeBlock(x,y,z,block.WOOD_PLANKS.id) for [x,y,z] in blocksPos]
doorShape = minecraftstuff.MinecraftShape(mc, doorsPos, doorsBlocks)

def openDoor():
 while True:
  pos = mc.player.getTilePos()
  if pos.x in range(doorsPos.x-3, doorsPos.x+1) and abs(pos.y-doorsPos.y)<2 and abs(pos.z-doorsPos.z)<2:
     doorShape.moveBy(0, -4, 0)
     time.sleep(5)
     doorShape.moveBy(0, 4, 0)

Thread(target=openDoor).start() 
from mcpi import minecraft
from pathlib import Path

mc = minecraft.Minecraft.create()
if not (Path(".house_coordinates.txt").is_file()):
    housePos = mc.player.getTilePos()+minecraft.Vec3(0,0,10)
    f = open(".house_coordinates.txt", "w")
    f.write("{},{},{}".format(housePos.x, housePos.y, housePos.z))
    f.close()
else:
    f = open(".house_coordinates.txt", "r")
    x,y,z = f.readline().split(',')
    f.close()
    housePos = minecraft.Vec3(int(x),int(y),int(z))

mc.setBlocks(housePos.x+0,  housePos.y+0,  housePos.z+0,
             housePos.x+10, housePos.y+10, housePos.z+10, 17)
mc.setBlocks(housePos.x+1,  housePos.y+1,  housePos.z+1,
             housePos.x+9,  housePos.y+9,  housePos.z+9, 0)
mc.setBlocks(housePos.x+4,  housePos.y,    housePos.z,
             housePos.x+6,  housePos.y+3,  housePos.z, 0)
mc.setBlocks(housePos.x+1,  housePos.y+5,  housePos.z,
             housePos.x+2,  housePos.y+6,  housePos.z, 0)
mc.setBlocks(housePos.x+8,  housePos.y+5,  housePos.z,
             housePos.x+9,  housePos.y+6,  housePos.z, 0)

Pobudujemy z pętli, pokażę młodemu minecraftstuff, kształty i obiekty 3d i myślę, że mu się spodoba. A poniższe, to drzwi na fotokomórkę. Wystarczy podejść ;).

auto&&… args.

Kiedyś zastanawiałem się nad tym, co jeszcze można będzie zrobić z auto i o co całe to wielkie halo.

Auto może być użyte do dedukcji typów uniwersalnych referencji pakietu parametrów, w funkcjach o dowolnej liczbie parametrów. Brzmi strasznie ale wbrew pozorom nie jest to takie skomplikowane, a jeśli miałbym wskazać jedną nowinkę w standardzie nowego c++, wybrałbym właśnie to, choć tych fajnych rzeczy w standardzie po c++11 jest mnóstwo:

#include <iostream>

using namespace std;

int main()
{
        auto f = [] (auto&&... args) {
            ((cout << args << " "), ...);
            cout << endl;
        };

        f(1, 2.3, "cztery");
}

~/cpp_fun>$ ./”variadic_lambdas1″
1 2.3 cztery

Niedługo mam nadzieję będzie to działać nie tylko dla funkcji lambda, ale też dla „zwykłych” funkcji.

std::variant, std::any i ranges-v3 czyli prawie jak w Pythonie.

Ostatnio w ramach „się odstresowywania” czytam sobie o zmianach w standardzie c++17 i c++20 (wielkie dzięki Bartkowi Filipkowi za jego bloga). Dużo tego, ale zmiany są ogromne i dzisiejszy C++ jest po prostu fascynujący. Nawet nie komentuje poniższego kodu. Wklejam tu dla samego siebie i powiem tylko, że jak g++/clang++ będą z c++20 zgodne, to ja już nie chcę nic więcej. Może jeszcze tylko ogarnięcie parallelstl. Intel-tbb działa pięknie. Tylko jeden cytat apropos:

„Do you know how much of the processing power of a typical desktop machine we can utilize using only the core version of C++/Standard Library? 50%, 100%? 10%?[…] we can usually access only around 0,25% with single-threaded C++ code and maybe a few percent when you add threading from C++11/14.” (Sean Parent).

Przyznam szczerze, że nieco otworzyło mi to oczy:

GPU power CPU vectorization CPU threading Single Thread
75% 20% 4% 0,25%
#include <iostream>
#include <vector>
#include <variant>
#include <any>

using namespace std;

template <typename T1, typename T2>
class T {
    T1 a; T2 b;    
    public:
    T(T1 a, T2 b) : a(a), b(b) {}
    void show() {        
        cout << a << b;
    }    
};

int main() {
    T<char, const char*> t1('C', "++");
    T<int, const char*> t2(17, " nie taki straszny ;)\n");
    T<string, int> t3("66.", 6);

    typedef variant<T<char, const char*>, T<int, const char*>, T<string, int>> var_t;
    vector<var_t> var_v {t1, t2, t3};

    auto callShow = [](auto& obj) { obj.show(); };

    for (auto& obj : var_v)
        std::visit(callShow, obj);   

    cout << endl << "------" << endl;

    vector<any>anyv {1, 2.0, "trzy"};

    for(auto &i: anyv) {
        if (i.type() == typeid(int))
            cout << any_cast<int>(i) << endl;     
        if (i.type() == typeid(double))
            cout << any_cast<double>(i) << endl;     
        if (i.type() == typeid(const char *))
            cout << any_cast<const char *>(i) << endl;   
        if (i.type() == typeid(string))
            cout << any_cast<string>(i) << endl;    
    }
}

PS E:\cpp_fun> .\templ1.exe
C++17 nie taki straszny 😉
66.6
——
1
2
trzy

#include <iostream>
#include <range/v3/all.hpp> 
#include <string>
#include <vector>

using namespace std;
using namespace ranges;

int main()
{
    auto print = [](auto &i) {cout << i << " "; };    
    auto even = [](int i){ return 0 == i % 2; };
    auto square = [](int i) { return i * i; };
    auto end = ranges::end;

    vector<int> ints = views::ints(1, 50)  | views::filter(even) | 
                       views::transform(square) | views::slice(end-5, end) | 
                       ranges::to<vector>();

    ranges::for_each(ints, print);

    auto chars = views::iota('a', 'z'+1) | views::reverse
                       | ranges::to<vector>();
    
    ranges::for_each(chars, print);

    auto t = views::ints(0, 50) | 
         views::filter([](int i) { return i<20 ? true : false; }) | 
         ranges::to<vector>();
    
    ranges::for_each(t, print);
    cout << endl;    
}

PS E:\cpp_fun> .\ranges2.exe
1600 1764 1936 2116 2304 z y x w v u t s r q p o n m l k j i h g f e d c b a 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

BTW: dzięki Bartkowi odkryłem, że nie boję się już lambd, a nawet je trochę lubię 😉

Flutter for the Web.

Flutter for the Web

Aplikacje Flutter w przeglądarce? Właśnie na Google Developers Days pojawiło się info o tym, że Flutter for the Web znalazło się w głównym repozytorium. Wystarczy zmienić flutter channel ze stable na master aby zgodnie z opisem tutaj odpalić aplikację w przeglądarce.  Drugi brakujący feature: Foreign Function Interface (FFI). Przyznam szczerze – te zmiany robią wrażenie. Kawał ciekawej i przydatnej technologii. Jedno jest pewne – będzie się działo!

Flutter for the Desktop

Dart i Flutter.

Fajny ten Dart. Jakoś wcześniej nie mogłem się do niego przekonać, ale moje ostatnie eksperymenta z ES6 trochę mi to ułatwiły.

Analizator i system typów robi spore wrażenie. OOP w Dart to chyba najlepsze co do tej pory widziałem – dziedziczenie, interfejsy, klasy i metody abstrakcyjne, mixins, overrides, listy inicjalizacyjne konstruktora, prosta inicjalizacja argumentów nazwanych konstruktora, po prostu trzeba to zobaczyć. Flutter to temat na kilka osobnych wpisów ;), a StatefullWidget mnie zachwycił. Poniżej mój pierwszy eksperyment – kolejne wcielenie apki do sterowania esp8266 za pomocą http request.

Kliknij w obrazek powyżej żeby zobaczyć plik źródłowy.

Widać w tym przyszłość i to raczej pozytywną, zarówno dla użytkowników, jak i programistów. A i jeszcze jedno – nie sądziłem, że to kiedyś powiem, ale Visual Studio Code to świetny edytor ;).

Mój plan na ten rok:

  • ogarnąć lepiej współczesną Javę (co tam w wersji >=10?)
  • dobrze poznać Dart + Flutter (coś mi mówi, że się przyda)
  • Kotlin – coś mi nie leży, muszę zidentyfikować dlaczego. Może to tylko kwestia przyzwyczajenia do niektórych elementów składni.

Aktualizacja: oficjalnie zaliczam Dart i Flutter do grona zjawisk i technologii, które mnie zafascynowały (patrz strip na górze ;)).

Kliknij w obrazek powyżej żeby zobaczyć plik źródłowy.

Syncthing + AspeQt-1.0.62.

Z najnowszej wersji AspeQt-1.0.62 (dostępna w Google Play i na Github) jestem wyjątkowo dumny pomimo, że nie ma w niej żadnych rewolucyjnych zmian:

  • Przebudowane z Qt-5.9.7 (min. API 16 = Android 4.1).
  • Poprawione błędy sterownika ftdi – działa z HSINDEX 0 = 115200bps.
  • Sprawdzanie dostępu do karty SD na Mashmallow i nowszych wersjach Androida

Jednak ta druga pozycja… to chyba najbardziej dołujący jak dotąd błąd z jakim przyszło mi się zmierzyć. Ale o co chodzi? Na nie-rootowanym Androidzie jedyna sensowna opcja obsługi portu szeregowego, a taki jest potrzebny do emulacji urządzeń SIO w 8-bitowym Atari, to tzw. usb-host mode i próba dogadania się z najpopularniejszym i najbardziej sensownym układem pozwalającym na taką komunikację, czyli FT232R firmy FTDI. Firma wypuściła w tym celu bibliotekę d2xx dla Androida, jednak nie jest ona zbyt szybka, a dodatkowo, licencja jest co najmniej problematyczna. Dawno temu odkryłem więc usb-serial-for-android. Dostęp do obiektów Javy w Qt5 i vice versa opisywałem już wcześniej – wystarczy wspomnieć, że bez JNI nie da rady tego ogarnąć, ale co zaskakujące, działa to bardzo szybko i wydajnie. Pod warunkiem, że działa ;). No i tu zaczyna się demotywująca część historii. Na niektórych urządzeniach AspeQt śmigał aż miło, ale użytkownicy zaczęli zgłaszać, że pomimo prawidłowej obsługi usb-host, na niektórych całkiem porządnych urządzeniach z Androidem nie działa. Niestety usb-serial-for-android od dwóch lat praktycznie rozwija tylko jedna osoba – Kai Morich w swoim forku – i to dzięki niemu udało się problem rozwiązać.

A teraz o moim ostatnim odkryciu: syncthing. AspeQt z HSINDEX 0 śmiga całkiem szybko, więc podmontowałem w nim na androidowym TVBoxie katalog, który przez syncthing jest synchronizowany po Wifi z laptopem i telefonem. Dzięki temu wszystko co ściągnę z internetów mogę od razu, bez skomplikowanych operacji, uruchomić na moim 8-bitowym super sprzęcie ;). Syncthing jest świetny, darmowy i bez reklam. W archowych repozytoriach są gotowe paczki dla Linuksa, a w Google Play apk dla Androida. Długo czegoś takiego szukałem:

Godot 3.1.

Jedno, no dobra – dwa słowa – niesamowity projekt. Nie byłem nigdy chętny do zabawy silnikami growymi, ale Godot jest wyjątkowy. W zasadzie wszystko mnie w nim zachwyca – począwszy od formy dystrybuowania – jeden plik wykonywalny, przez możliwości i ogólnie sposób pracy z programem. No i jeszcze możliwości. I zapomniałem dodać jeden ficzer: możliwości.

Język skryptowy GDScript, dla Pythonisty – miodzio. Oprócz tego pełna swoboda, może być C#, GDNative i C++, jest i edytor wizualny. Szok. Wersja alpha (niestety wersja stabilna działa tylko na sprzęcie z OpenGL ES 3.0, ale w 3.1 będzie tylko GL ES 2.0) ani razu mi się nie wysypała, nawet jednego komunikatu o błędzie!

Powyższy screen to tutorial „Tiles and Animated Sprites” HartBeast-a (super się go słucha).

Obsługa ruchu i animacji sprajta to 27 linijek w GDScript.

extends KinematicBody2D

var motion = Vector2()
const UP = Vector2(0, -1)
const GRAVITY = 20
const SPEED = 200
const JUMP_HEIGHT = -550

func _physics_process(delta):
	motion.y += GRAVITY
	if Input.is_action_pressed("ui_right"):
		motion.x = SPEED
		$Sprite.flip_h = false
		$Sprite.play("Run")
	elif Input.is_action_pressed("ui_left"):
		$Sprite.flip_h = true
		$Sprite.play("Run")
		motion.x = -SPEED
	else:
		motion.x = 0
		$Sprite.set_animation("Idle")

	if is_on_floor():
		if Input.is_action_just_pressed("ui_up"):
			motion.y = JUMP_HEIGHT

	motion = move_and_slide(motion, UP)