Monthly archive for January, 2011

mgr inż. Xion

2011-01-31 18:18

Taka okazja zdarza się tylko raz, więc nie widzę powodów, by się tym nie chwalić ;P Udało mi się dzisiaj zdać egzamin dyplomowy, wobec czego oficjalnie zostałem magistrem inżynierem informatyki, specjalność Metody sztucznej inteligencji. Innymi słowy, mogę sobie przed imieniem i nazwiskiem dopisywać już nie trzy, a całe sześć liter :)

Kolejny symboliczny etap edukacji mam więc już za sobą. A przed sobą na pewno jeszcze mnóstwo nauki, bo w tej branży człowiek uczy się przecież przez całe życie…


Author: Xion, posted under Events, Studies » 27 comments

Duże i małe commity

2011-01-29 23:49

Mam ciekawą obserwację związaną ze sposobem używania różnych systemów kontroli wersji. Dokładniej mówiąc, chodzi o dość wyraźną różnicę w częstotliwości commitów między zwykłymi a rozproszonymi VCS-ami. Do tych pierwszych kod commituje się stosunkowo rzadko, ponieważ każda zmiana jest od razu widoczna w publicznym repozytorium. To ma zaś daleko idące konsekwencje, jak choćby natychmiastowa dostępność naszych modyfikacji dla innych osób pracujących nad projektem. Dlatego też trzeba starać się, aby nie wypuścić bubla. Minimalnym wymaganiem jest kompilowalność commitowanego kodu, a dobrze by było, żebyśmy też poddali go choćby wstępnemu testowaniu.

Gdy z kolei pracujemy z rozproszonymi systemami kontroli wersji, możemy teoretycznie robić dokładnie tak samo. Wówczas jednak nie tylko nie korzystamy z faktu posiadania lokalnego repozytorium, ale wręcz dodajemy sobie pracy. Osiągnięcie tego samego efektu (upublicznienia zmian) wymaga bowiem dwóch kroków – dodatkowym jest push, czyli synchronizacja z globalną bazą kodu. Będąc przyzwyczajonym do scentralizowanych VCS-ów można łatwo o nim zapomnieć.
Dlatego też wydaje mi się, że przy systemie rozproszonym warto nieco zmienić nawyki. Commitów można bowiem dokonywać częściej – znacznie częściej. Ponieważ żaden z nich nie wydostaje się automatycznie poza nasz komputer, nie muszą one dodawać kompletnych funkcjonalności albo w pełni poprawiać znalezione wcześniej błędy. Nie muszą działać. Ba, w zasadzie to nie muszą nawet się kompilować. Grunt, żeby zawierały modyfikacje, które wydają nam się warte zachowania i opatrzenia komentarzem. W praktyce w ciągu dnia można w ten sposób wykonać nawet do kilkudziesięciu commitów – w porównaniu do dwóch lub trzech w przypadku pracy z system scentralizowanym.

Czy taka częstotliwość dobrze wpływa na efektywność kodowania? Z początku nie byłem o tym przekonany, ale teraz widzę, że ma ona niewątpliwe zalety. Wykonując częste commity (a zwłaszcza opisując je), programujemy w sposób bardziej zdyscyplinowany. Trochę trudniej jest wtedy napisać wysublimowany, barokowo skomplikowany i ogólnie przekombinowany moduł albo takąż klasę. Mamy raczej większe szanse na to, że poczynimy jakieś realne postępy w pracy nad projektem. Dodatkowo częste “odhaczanie” wykonanych zadań i poczynionych postępów (nawet jeśli są bardzo drobne) jest bardziej motywujące niż oznaczanie zmian rzadkich a duże.
A co jeśli naszą przeszkodą w wykonywaniu częstych commitów jest brak weny twórczej odnośnie komentarzy do nich?… No cóż, wtedy zawsze można poszukać inspiracji tutaj ;-)

Tags: ,
Author: Xion, posted under Programming, Thoughts » 5 comments

Bo konsola to też okno

2011-01-26 9:48

Systemy uniksowe dość długo wyróżniały się znacznie lepszym wsparciem dla trybu wiersza poleceń niż Windows. To się w pewnym sensie zmieniło po powstaniu PowerShella, będącego co najmniej równie dobrą powłoką tekstową co bash czy zsh. Jednak przynajmniej w jednym aspekcie Linuksy wciąż mają tutaj wyraźną przewagę. Chodzi o graficzną otoczkę tekstowej konsoli.
W Windows programy konsole uruchamiane są wciąż w starym, w gruncie rzeczy obleśnym i w dodatku bardzo słabo i trudno konfigurowalnym oknie. Nie da się płynnie zmienić jego rozmiaru, zmaksymalizować, nie wspominając już nawet o otwieraniu kilku konsol w zakładkach tego samego okna. Możliwość modyfikacji czcionki czy też kolorów tekstu i tła też jest ograniczona i niewygodna. Dla porównania, okienka konsoli w systemach linuksowych są pod tym względem o wiele elastyczniejsze, zapewniając wszystkie te feature‘y i jeszcze sporo innych. W rezultacie interfejs tekstowy jest tam przyjemniejszy w obsłudze, nawet jeśli obiektywnie ustępuje możliwościami temu windowsowemu.

Czy da się coś na to poradzić, czyniąc windowsowe okienko konsoli bardziej znośnym?… Otóż można w pewnym – może nawet zadowalającym – stopniu to uczynić, lecz w tym celu trzeba się posłużyć zewnętrzną aplikacją. Chodzi mianowicie o open-source‘owy projekt Console2, będący okienkowym środowiskiem uruchamiania programów konsolowych. Technicznie działa on zapewne w podobny sposób, jak mój eksperyment sprzed prawie trzech lat, z tym że oczywiście robi to znacznie lepiej :) W międzyczasie zapewnia też sporą część funkcjonalności okienek terminala pracujących w X-Window, a mianowicie:

  • obsługę kilku zakładek z przełączaniem się między nimi za pomocą konfigurowalnych skrótów klawiszowych
  • wybór domyślnego shella (np. cmd, cygwin albo powershell), a także możliwość skonfigurowania kilku powłok uruchamianych automatycznie w osobnych zakładkach
  • zaawansowaną konfigurację wyglądu, aż do kształtu kursora włącznie
  • obsługę przezroczystości całego okna
  • zmianę jego rozmiaru poprzez przeciąganie za krawędź

i jeszcze kilka innych, w gruncie rzeczy naturalnych funkcji, których w tajemniczy sposób zabrakło w standardowym oknie konsoli w Windows. Jeżeli chodzi o stabilność i szybkość działania, to również prezentuje się nie najgorzej, chociaż zdarzają się problemy, np. z przesłaniem sygnału Ctrl+C w celu przerwania spamującego polecenia w rodzaju ls -r /. Małą wadą jest też konfiguracja, która może zająć trochę czasu i kłopotów, jeśli umieścimy program w standardowy folderze Program Files (wskazówka: należy wtedy poinstruować go, by zapisywał ustawienia w katalogu użytkownika).
Ogólnie jednak moje wrażenia co do tej aplikacji są w miarę pozytywne, lecz pamiętajmy, że rywal jest godny pożałowania ;) Tym niemniej polecam zapoznanie się z tym programem, jeśli dużo pracujemy z tekstowym shellem pod Windows.

Tags: , ,
Author: Xion, posted under Applications » 7 comments

Nie tylko pisanie kodu

2011-01-21 19:25

Gdyby było to fizycznie możliwe, chętnie przeprowadziłbym następujący eksperyment. Z odległej przeszłości – na przykład z połowy lat 90. poprzedniego stulecia – sprowadziłbym w obecne czasy dowolnego ponadprzeciętnie uzdolnionego programistę. Jak szybko odnalazłby się we współczesnym koderskim środowisku pracy?… W celu redukcji złożoności problemu poczyńmy daleko idące uproszczenia i pomińmy wszelkiego typu zmiany związane z postępem technologicznym (jak choćby nieporównywalnie większe znaczenie Internetu wtedy i teraz), a także modyfikacje, które zachodzą w samych językach programowania. Interesuje mnie raczej to, czy ów przybysz z przeszłości doceniłby i uznał za przydatne różnego rodzaju czynności i narzędzia pomocnicze, niebędące edytorem tekstu (lub IDE) ani kompilatorem, i pozostające w luźniejszym związku z samym pisaniem kodu jako takiego.

Jakby bowiem przyjrzeć się dokładnie wachlarzowi tych narzędzi, okazuje się, że jest on już całkiem szeroki. Programowanie, zwłaszcza zespołowe (jeżeli w ogóle istnieje jeszcze jakieś inne) już od dawna przestało ograniczać do tworzenia kodu i czasami wydaje się nawet, że czynność ta stała się poboczną. Coraz więcej czasu zajmuje praca z takimi narzędziami jak systemy kontroli wersji, systemy śledzenia błędów (issue tracking systems), narzędzia automatyzujące proces kompilacji, programy do statycznej analizy kodu, systemy zdalnej kompilacji i pewnie mnóstwo jeszcze innych wynalazków, z którymi nie miałem dotąd okazji się zetknąć.
Między innymi dlatego nie potrafię jednoznacznie określić, czy uważam je wszystkie raczej za przydatne czy raczej za zbędne. Wiążące opinie mogę jak dotąd wyrazić tylko o niektórych.

Tym niemniej ciekawi mnie również, czy w dziedzinie wspomagania kodowania (czy ogólnie pracy nad projektami) od strony zautomatyzowanych narzędzi da się wymyślić coś jeszcze…

Obrazki dziewięciołatkowe

2011-01-13 22:50

Jako dzisiejszą ciekawostkę chcę przedstawić pewną technikę, która może być przydatna przy implementacji systemu GUI na przykład na potrzeby gier. Zazwyczaj bowiem elementami interfejsu są różnego rodzaju kontrolki, które – chociaż nierzadko nie przypominają klasycznych widgetów w rodzaju przycisków czy pól tekstowych – posiadają prostokątne obramowanie. Ta ramka musi mieć jedną istotną cechę: powinna dopasowywać się do rozmiaru kontrolki, dopuszczając elementy o dowolnie dużych wymiarach. A jednocześnie powinna ona być utrzymana w odpowiednim stylu graficznym, zwykle spójnym z resztą aplikacji lub gry.

Sposobem na sprostanie tym wymaganiom jest podział prostokąta kontrolki na dziewięć części, różniących się zakresem skalowania, jakiemu są poddawane, gdy kontrolka zmienia rozmiary. Wśród tych części możemy mianowicie znaleźć następujące:

  • cztery rogi, które w ogóle nie zmieniają rozmiarów i są zawsze rysowane tak samo
  • dwie krawędzie poziome (górna i dolna) oraz dwie pionowe (lewa i prawa), które zmieniają tylko jeden ze swoich wymiarów, odpowiednio: szerokość lub wysokość
  • środek, któremu zmieniają się zawsze oba wymiary

Żeby więc poprawnie narysować naszą kontrolkę, potrzebujemy w ogólności aż dziewięciu różnych obrazków. Oczywiście niekiedy będzie ich mniej, jeśli sprytnie posłużymy się obrotami i odbicia obrazów. Prawie na pewno jednak będzie to więcej niż jeden sprite i każdy z nich trzeba będzie jakoś oznaczyć i zapisać razem z naszą aplikacją, wymyślając do tego mniej lub bardziej skomplikowaną konwencję.

To, co chcę przedstawić, to w gruncie rzeczy taka właśnie konwencja. Nazywa się ona nine-patch images, czyli tytułowe obrazki dziewięciołatkowe. Pochodzi ona z systemu Android i jest jedną z wielu może mało efektownych, ale pomysłowych rozwiązań, ukrytych w jego API.
Idea jest dość prosta. Jeśli mamy już obramowanie w postaci obrazka otoczonego nim prostokąta, nie musimy wycinać z niego poszczególnych części i zapisywać ich osobno. Zamiast tego zaznaczamy je bezpośrednio na obrazku, dodając wpierw jednopikselowe, czarno-białe obramowanie. Czarny fragment określa część podlegającą skalowaniu (czyli środek), zaś biały – niezmieniające rozmiaru mu rogi prostokąta. Najlepiej widać to na ilustracji po lewej stronie.
Można na niej zauważyć, że do określenia obszaru skalowania wystarczy zasadniczo jeden dodatkowy rząd i jedna dodatkowa kolumna pikseli. Pozostała część dodatkowego obramowania może nam wtedy posłużyć do zdefiniowania obszaru treści, czyli tego fragmentu kontrolki, który będzie mógł być wypełniony tekstem i innymi elementami, ustawionymi jako jej zawartość. Obszar treści nie musi pokrywać się z obszarem skalowania, co z kolei jest widoczne na obrazku po prawej stronie.

Z tak przygotowanej grafiki wciąż możemy w miarę łatwo odczytać programowo potrzebne informacje o sposobie rysowania obramowania kontrolki i wypełniania jej treści. Rozwiązanie to ma przy tym tę oczywistą zaletę, że przygotowanie takiej grafiki jest z pewnością mniej pracochłonne i kłopotliwe niż tworzenie od 3 do 9 osobnych sprite‘ów i podawanie informacji o obszarze treści w jakiegoś rodzaju pliku konfiguracyjnym.
Jeśli więc ktoś w ramach silnikologii stosowanej zajmuje się implementacją modułu GUI, może rozważyć zastosowanie podobnego systemu :)

Tags: , ,
Author: Xion, posted under Programming » 7 comments

DOD organizuje dane

2011-01-08 18:56

Moi koledzy-częściowo-po-fachu, czyli programiści silników gier, wymyślili niedawno magiczny trzyliterowy akronim DOD – skrót od Data-Oriented Design, czyli projektowanie oparte o dane. Oczywiście określenie ‘niedawno’ jest względnym i pewnie wielu z nich orzekło by, że DOD jest z nimi już całkiem długo. Każdy mem potrzebuje jednak czasu na rozprzestrzenienie się, a w przypadku tego fala tweetów na jego temat dotarła do mnie dopiero niedawno. Niedługo potem rzecz wydała mi się cokolwiek podejrzana.

Podstawowe pytanie brzmi rzecz jasna: o co w tym właściwie chodzi?… Ponieważ mówimy o programowaniu gier, to odpowiedź jest jasna: jeśli nie wiadomo o co chodzi, to chodzi o wydajność. W zaawansowanych grach czasu rzeczywistego mamy do czynienia z ogromną ilością danych, na których trzeba wykonać wiele, często skomplikowanych operacji, a wszystko to jeszcze musi być zrobione dostatecznie szybko, aby możliwe było pokazanie na ekranie kolejnej klatki bez widocznych przycięć. Dlatego też już dawno zauważono, że kodowanie “blisko sprzętu” się opłaca, bo pozwala maksymalnie wykorzystać jego możliwości.
To oczywiście nakłada na kod pewne wymagania oraz stwarza konieczność zwrócenia uwagi na rzeczy, którymi “normalnie” nie ma potrzeby się zajmować. Ładnym przykładem jest chociażby zarządzanie pamięcią. W wielu językach jest ono albo kompletnie pomijalne (garbage collector), albo sprowadza się do dbania o to, aby każdy zaalokowany blok był w końcu zwolniony. Gdy jednak stawiamy na wydajność, powinniśmy też zainteresować się szybkością samej operacji alokacji oraz takim rozmieszczeniem przydzielanych bloków, aby komunikacja na linii procesor-pamięć odbywała się z jak najmniejszą liczbą zgrzytów.
Ten i wiele podobnych szczegółów platformy sprzętowej powodują, że pisanie efektywnego kodu w silnikach gier to często dość literalne postępowanie według zasady Do It Yourself, połączone z ignorowaniem części feature‘ów wysokopoziomowych języków programowania, o których wiadomo, że negatywnie odbijają się na wydajności. Cóż, życie; nie ma w tym nic zaskakującego. Myślę, że każdy co bardziej zaawansowany programista zdążył zdać sobie sprawę z tego, że wszelkie koderskie udogodnienia związane z podniesieniem poziomu abstrakcji mają swój koszt liczony w dodatkowych cyklach procesora (i nie tylko). Rezygnacja z nich jest więc dobrym posunięciem, jeśli chcemy te “stracone” cykle odzyskać.
Robiąc to, będziemy mieli ciastko, ale już nie będziemy mogli go zjeść – a to oczywiście nie jest przyjemne. I po części zapewne stąd wzięło się pojęcie DOD, które nie odnosi się do niczego w gruncie rzeczy nowego, ale pozwala łatwiej odnosić się do tego rodzaju koderskich praktyk poprzez nadanie im nazwy. A przy okazji – jak mi się wydaje – w jakiś nie do końca wytłumaczalny sposób redukuje dysonans poznawczy programistów silników gier, którzy świadomie muszą pozbawiać się możliwości przestrzegania “jedynie słusznych” zasad pisania kodu.

Jak dotąd wszystko jest w gruncie rzeczy bardzo ładne i sensowne, i bez problemu zgadzam się z postulatami Data-Oriented Design tam, gdzie się one aplikują. Zgadzam się nawet z tą domniemaną ukrytą motywacją, zwłaszcza że sam nieraz narzekałem na owe “jedynie słuszne” rady. Za to nijak nie mogę pojąć, dlaczego następnym krokiem – po wynalezieniu pojęcia DOD – był mniej lub bardziej frontalny atak na programowanie obiektowe, określane nieco bardziej znanym (ale naturalnie również trzyliterowym) akronimem OOP.
Nie, nie chodzi o to, że programowanie obiektowe jest doskonałe – bo nie jest, nie było, nigdy nie będzie i nawet nie aspiruje do miana finalnego rozwiązania dla dowolnego problemu (już nie wspominając o tym, że takowe po prostu nie istnieją). Rzecz w tym, że zwolennicy DOD (DOD-a? :]) w nieprzemyślany sposób wybrali sobie przeciwnika, nie zauważając, że jest on paradygmatem zupełnie innego rodzaju niż ich własny. A to przecież takie proste:

  • OOP sugeruje, że naturalną architekturą dla systemów jest ta oparta o obiekty, będące mniej lub bardziej abstrakcyjnymi bytami, połączonymi relacjami i wchodzącymi ze sobą w interakcje
  • DOD wskazuje na to, że głównym zadaniem kodu jest transformacja danych w inne dane i że optymalizacja procesu przetwarzania danych jest priorytetem przy tworzeniu wewnętrznej struktury programu

Widać to, prawda?… Miedzy powyższymi dwoma podejściami nie tylko nie ma sprzeczności. One są od siebie po prostu niezależne, co oznacza również, że mogą występować razem w jednym programie.


“Mapa nie jest terytorium.”

Jeśli Data-Oriented Design koniecznie potrzebuje jakiegoś przeciwnika, to są nim raczej inne xOD-y, których jest już przynajmniej kilka, chociaż wiele nie zostało jeszcze nawet nazwanych. (Dobry przykład to projektowanie oparte o user experience, czyli wrażenie użytkownika, gdzie priorytetem jest m.in. responsywność, nie będąca wcale synonimem wydajności). To, co piewcy DOD zdają się krytykować w swoich publikacjach, to jakieś “projektowanie oparte o eleganckie abstrakcje”, czyli pisanie kodu, który jest sztuką dla sztuki: ładnie wygląda (w założeniu), ściśle trzyma się założeń używanego paradygmatu przy jednoczesnym eksploatowaniu wszelkich jego “zdobyczy” (czyli np. wzorców projektowych). I chociaż bywają w swoich wysiłkach niezwykle twórczy (w prezentacjach z tego tematu spotkałem nawet cytaty z Baudrillarda), to nie zmienia to faktu, że kopią leżącego (czy raczej biją martwego konia, jakby to powiedzieli Amerykanie ;-)). Bo jeśli ktoś naprawdę posuwa się do takich absurdów jak czteropoziomowa hierarchia dziedziczenia obiektów gry, to znaczy że ma znacznie poważniejsze problemy niż okazjonalny cache miss :)

To jest częściowo nietypowa notka

2011-01-04 23:55

Notka rozpoczyna się od stwierdzenia jednego lub kilku raczej oczywistych faktów, których autor w dość sztampowy sposób używa w celu rozpoczęcia tematu. Najczęściej przywołuje w tym celu swoje nieodległe w czasie doświadczenia związane z tymże tematem, które czasami prowadzą do mniejszej lub większej (częściej mniejszej) zmiany przekonań. Reszta pierwszego paragrafu zazwyczaj podporządkowana jest dobrnięciu do jego końca w sposób, który sprawi wrażenie spójnego wywodu. Jednocześnie jednak sam koniec jest celowo trochę kontrastowy, co ma na celu lekkie zaskoczenie czytelnika i przekonanie jego podświadomości, że lektura reszty może nie być wcale taką stratą czasu na jaką wygląda.


Gdyby był związany bardziej,
miałby bardziej odpowiedni podpis.

Środkowa część służy zrazu dalszemu rozwinięciu opisu zjawiska, którym to w tym przykładzie jest pewien specyficzny rodzaj humoru. Często jest to wspomagane odpowiednim formatowaniem tekstu, aplikowanym zazwyczaj do pojedynczych słów, które autor z jakiegoś dziwnego powodu uważa za ważne, zwykle zresztą błędnie. Nierzadko są to jego własne wymysły terminologiczne (jak choćby określenie wspomnianego rodzaju humoru jako metasatyry), wyróżnione formatowaniem tylko dlatego, iż bez niego nikt nie zwróciłby na nie najmniejszej uwagi. Byłoby to zresztą niezwykle pożyteczne, jako że ich adekwatność pozostawia zwykle wiele do życzenia.
Następnie przytaczane są różne argumenty lub przykłady tudzież elementy opisywanego zjawiska, wybrane zazwyczaj spośród tych, do których prowadzą linki z trzeciej strony wyników wyszukiwania w Google (albowiem powszechnie wiadomo, że wszyscy zatrzymują się na pierwszej, więc w ten sposób uzyskuje się gwarancję oryginalności źródeł). Pozycje te bardzo często uformowane są w postać listy wypunktowanej, ponieważ nadaje im ona pozory dobrze zorganizowanej struktury (co natychmiast podnosi wiarygodność) przy jednoczesnym unikaniu zbytniego formalizmu, nieodłącznej cechy listy numerowanej.

  • Pierwszy element jest niemal na pewno w jakiś sposób domyślny, oczywisty, sztampowy lub nawet nieco nudny. Dzięki temu czytelnik może łatwo odnieść go do swoich własnych doświadczeń, bo na pewno się z nim zetknął, jeśli tylko ma cokolwiek wspólnego z opisywanym zagadnieniem. Kiedy chodzi o ów specyficzny i interesujący rodzaju humoru ochrzczony mianem metasatyry, za pierwszy przykład posłuży artykuł na stronie internetowej – głównie dlatego, że tekst może być uznany za względnie nudny, kiedy porównamy go do występujących dalej obrazków i filmów.
  • Pozycje środkowe są celowo najciekawsze, najprzydatniejsze i najbardziej interesujące. W ten sposób autor chce nagrodzić tych zdesperowanych wytrwałych czytelników, którzy z jakiejś niepojętej przyczyny czytają notkę w całości zamiast zachować się bardziej racjonalnie, tj. rzucić okiem na wstęp i zobaczyć, czy zakończenie było trafne. Dla nich przewidziane są najlepsze przykłady ilustrujące zjawisko, czyli generyczny reportaż programu informacyjnego oraz trailer dowolnego oscarowego filmu ostatnich lat.
  • Końcowe przykłady bywają doprawione szczyptą humoru, który okazjonalnie nawet wydaje się w pewien sposób zabawny. O wiele częściej jednak taki nie jest i dlatego czytelnik z ulgą znajduje te przypadki, w których autor całkowicie z niego zrezygnował i zamiast tego posłużył się odpowiednio relewantnym obrazkiem z xkcd. (Jako iż powszechnie wiadomo, że xkcd ma pasujący obrazek do dokładnie wszystkiego).

Ostatni paragraf lub dwa są aż do bólu przewidywalne, gdyż trudno spodziewać się tu czego innego niż podsumowania. Wskazuje ono na główną linię rozumowania z części środkowej lub też zbiera wspólne cechy podanych przykładów, takie jak punktowanie powtarzalnych, szablonowych form imitowanych przez metasatyrę przy jednoczesnym dokładnym trzymaniu się tychże form. Jeśli autor ma akurat dobry dzień, uda mu się celnym zdaniem wskazać sedno zagadnienia (a więc efekt komiczny spowodowany paradoksalnością wspomnianych cech tego humoru), jednakże zawsze będzie to inne zdanie niż to, które w jego zamyśle miało takim być. Definitywne zakończenie czytelnik przyjmuje z westchnieniem ulgi, aczkolwiek zupełnie bez zaskoczenia, bowiem było ono dla niego od początku całkiem oczywiste.

Niekiedy notka uzupełniona jest o komentarz małym drukiem, o treści która w jakiś pokrętny sposób odnosi się do całości notki. Na pewno aczkolwiek nie wspomina ona o nadużywaniu odrobinę zbyt wysublimowanego języka oraz występujących gromadnie, w gruncie rzeczy niepotrzebnych linkach do Wikipedii.

Tags: ,
Author: Xion, posted under Culture, Internet, Website » 15 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.