Włączasz grę, robisz nowy save i nazywasz swoją firmę. Potem możesz opracować technologię silników i zbiorników paliwa, a następnie zaprojektować i nazwać własną rakietę. Oczywiście nie wystartujesz rakiety, jeśli jej nie masz, dlatego musisz też ją najpierw zbudować. Ta cała „ciężka” praca opłaci się, gdy będziesz miał możliwość kliknięcia magicznego przycisku, który wyniesie Twoją rakietę prosto w przestrzeń kosmiczną, a jeśli zainwestowałeś w podwozie, to rakieta będzie mogła również wrócić i spektakularnie wylądować na barce.
Brzmi dobrze? O tym właśnie jest EarthX. Gra pozwoli Ci na rozwijanie najróżniejszych technologii rakietowych i ogólnym zarządzaniem firmą (a w przyszłości również planetą!).
Moim zdaniem gra jest trochę za prosta, dlatego do gry zostaną wprowadzone nowe systemy projektowania, budowania oraz wysyłania rakiety w kosmos. Celem jest podniesienie trudności gry poprzez dodanie większej ilości nowych funkcji oraz nowej zawartości do odblokowania. Dodatkowo ciekawym urozmaiceniem gry będzie dodanie efektów pogodowych (rakieta nie będzie mogła startować przy silnym wietrze), dodanie konkurencji i zleceniodawców oraz oczywiście dodanie różnych zdarzeń w grze (np. rakieta może wybuchnąć).
EarthX to nie jest symulator rakiet. To nie jest nawet podobne do jakiegokolwiek symulatora. EarthX to bardziej taki strategiczno-ekonomiczny sandbox, w którym będziemy mogli rozwijać nie tylko rakiety, ale także różne systemy transportowe na ziemi (Tesla i Boring Company, wait for it ( ͡° ͜ʖ ͡°)).
Gra jest teraz w bardzo wczesnej fazie rozwoju (Early Access), więc nie posiada jeszcze ona wielu funkcji. Jednakże kupując moją grę za 3$ wspomożesz jej rozwój w przyszłości. Zebrane środki przeznaczę na przyszłe wydanie gry na Steamie oraz opłacenie grafika, który wykona różne ikonki modułów do gry.
Kup grę tutaj: kliknij, aby przejść na itch.io
]]>
Język Lua nie udostępnia wprost możliwości działania na obiektach. Jest za to jedna z możliwości, jak taki sposób uzyskać. Z pomocą przychodzą nam metatablice . Jako, iż wikipedia nie jest zbyt wymowna na ich temat, pozwolę sobie skonstruować prostą do zrozumienia definicję na potrzebę ów artykułu.
Metatablice są to tablice zawierające określony zestaw zachowań, na które jest w stanie zareagować wartość. Każda wartość ma swoją metatablicę, która reguluje to, w jaki sposób np. liczba 5 zareaguje na próbę wykonania na niej operacji dodawania.
Znając teraz uproszczoną definicję metatablicy, możemy przejść przez kawałek teorii związanej z tym zagadnieniem. W Lua mamy do czynienia z prostą dwuwymiarową tablicą, która jest wywoływana przez interpreter w każdym przypadku, gdy coś wyda mu się nie do końca poprawne. Weźmy sobie za przykład wartość nieliczbową, np. tablicę jednowymiarową. Co się stanie, gdy nasza tablica zostanie operandem dodawania? Interpreter zwróci uwagę na to, że chcemy dodać do czegoś wartość nieliczbową i zwróci się do metatablicy podając zdarzenie
add
. Metatablica zwróci za to metametodę (wartość pod kluczem
__add
) odpowiadającą temu zdarzeniu, co pozwoli nam (lub nie, w zależności od tego czy metametoda istnieje) wykonać operację dodawania na tej wartości. Zrzucając szatę możliwego niezrozumienia pojęć pozwolę sobie wytłumaczyć użyte przeze mnie dwa nowe pojęcia.
Zdarzenie
to po prostu klucz tablicy, a
metametoda
to funkcja, która zostanie wykonana w przypadku wywołania danego zdarzenia. Brzmi znajomo? Już za moment wszystko powinno stać się zrozumiałe.
Szukając porówniania do innych języków, przykładem dla nas mogą okazać się przeciążenia operatorów w języku C#. Idea działania jest praktycznie taka sama.
Jeżeli teraz połączymy te informację z inną, która mówi nam, że te metatablice można, tak jak w językach typu C#, przeciążać, to dojdziemy do wniosku, że z dużą dozą prawdopodobieństwa uda nam się napisać bardzo dobrze działający „kod zorientowany obiektowo”. Możemy teraz przejść do praktycznej części.
Rozpocznijmy naszą praktykę od utworzenia metatablicy oraz nadpisania jej zdarzenia.
Utworzyliśmy teraz metatablicę
Account
, której wartość zdarzenia
index
ustawiliśmy na nią samą. Dzięki czemu obiekty będą mogły korzystać z metod zdefiniowanych w tej tablicy. Na tym jednak nie poprzestajemy.
Tym razem zdefiniowaliśmy metodę
create
, która pozwoli nam na utworzenie „obiektu” naszej tablicy. Nasz trik polega na utworzeniu pustej tablicy, której wstrzykniemy naszą spreparowaną metatablicę. Następna linijka odpowiada za „konstruktor” naszego obiektu, czyli zapisanie właściwości
balance
przekazanej z wywołaniem metody
create
. Na sam koniec zwracamy nowo utworzony „obiekt”. Nie mielibyśmy jednak obiektu, gdyby nie chociaż jedna metoda poza konstruktorem. Utwórzmy więc takową.
Prosta metoda umożliwiająca podanie w argumencie liczby o którą uszczuplimy właściwość
balance
naszego obiektu. Skorzystaliśmy sobie w niej z parametru self, który umożliwia nam ominięcie odniesienia do globalnej wartości
Account
. Jest to dobra praktyka, która warto sobie przyswoić.
Jeżeli jesteś osobą, która lepiej czuje się z użyciem parametru
this
niż
self
, istnieje możliwość zmiany tego parametru. W tym celu, przy tworzeniu definicji metody, zamiast
:
korzystamy z operatora
.
. Wówczas w pierwszym argumencie możemy wpisać
this
lub cokolwiek innego, dzięki czemu zmienna ta stanie się reprezentacją naszego obiektu w tej definicji.
Do testów starczy nam to w zupełności. Utwórzmy teraz jeden obiekt i skorzystajmy z metody.
Prześledźmy teraz działanie tych dwóch linijek kodu, aby lepiej zrozumieć co się stanie. Pierwsza linijka to po prostu utworzenie obiektu na bazie klasy
Account
. Działanie tej metody wytłumaczyliśmy sobie parę akapitów wyżej. Druga linijka natomiast nie jest tak oczywista jak mogłoby się wydawać. Odwołanie do metody tablicy
acc
zwróci błąd, ponieważ nic takiego nie istnieje. Interpreter wie natomiast, że
acc
to tablica, tak więc podejmie próbę wyszukania indeksu tej tablicy. I tutaj wkracza do akcji przygotowana przez nas metatablica, która umożliwia nam dostanie się do zasobów tablicy
Account
. I tym sposobem utworzyliśmy nasz pierwszy pełnoprawny obiekt w języku Lua!
Czy istnieje jednak prostsze podejście do obiektowości w Lua? Okazuje się, że tak. Co więcej, być może uda nam się nawet użyć dziedziczenia! To jednak materiał na kolejny artykuł.
Artykuł bazujący na zawartości strony: http://lua-users.org/wiki/SimpleLuaClasses
]]>Machine Learning (pol. uczenie maszynowe, samouczenie się maszyn, systemy uczące się) jest jedną z aktualnie najlepiej prosperujących dziedzin w świecie IT.
W 1959 Alan Turing i Arthur Samuel amerykańscy pionierzy w dziedzinie gier komputerowych i sztucznej inteligencji ukształtowali współczesny termin machine learning .
The field of study that gives computers the ability to learn without being explicitly programmed.
~ Arthur Samuel
Oznacza to możliwość nauki programu komputerowego na podstawie wcześniej przygotowanych danych w celu rozwiązania konkretnego problemu bez znania sposobu na osiągnięcie go, czyli wcześniejszej listy kroków którą ma wykonać program. Oparte jest to o zestaw gotowych algorytmów z podanych dziedzin matematyki:
Machine Learning działa w oparciu o sieci neuronowe wzorowane na ludzkich. Powyższy schemat sieci neuronowej pokazuje jej uproszczony sposób działania. Składa się ona z neuronów . Każdy neuron jest wprowadzany przez dendryty od innych neuronów przenosząc sygnały otrzymywane z innych neuronów przez synapsy . Na grafice powyżej czerwone neurony stanowią warstwę wejściową, niebieskie – warstwę ukrytą, a zielone – warstwę wyjściową. Ilość ukrytych warstw jest zależna od tzw. głębokości , im głębsza tym bardziej skomplikowane są połączenia między nimi.
Podczas trenowania własnej sieci neuronowej dane są przekazywane od neuronu do neuronu, każdy kolejny może zapamiętywać (Recurrent Neural Network) wynik poprzedniego i przekazywać coraz to lepsze dane. Im więcej razy będziemy trenować taką sieć tym mniej błędów i lepszy wynik.
Ta sieć bardzo dobrze klasyfikuje proste rzeczy, ale nie pamięta o poprzednich czynnościach/zmianach oraz ma nieskończone warianty wyników.
W tym przypadku sieć pamięta poprzednie zmiany i ma skończony wariant wyników.
Ten sposób uczenia polega na stworzeniu modelu danych, wejściowych czyli próbek (samples) wraz z modelem wyjściowym na przykład etykiet. Gdy wytrenujemy naszą sieć będziemy mogli prawidłowo przypisać wyjście dla obiektu którego dotychczas nie było na wejściu. Nasza sieć uczy się w oparciu o nasz model tzw. dataset .
Tym razem sieć nie otrzymuje danych wyjściowych (etykiet), więc sama musi znaleźć odpowiedni sposób, aby uzyskać dane wejściowe.
W uczenie przez wzmacnianie sieć działa bez określonych danych wejściowych i wyjściowych. Jedyne informacje jakie otrzymuje to tzw sygnał wzmocnienia, który może być pozytywny (nagroda) w przypadku podejmowania trafnych decyzji lub negatywny w przypadku mylenia się (kara). Jest to całkiem niezły sposób na naukę naszej AI grania w proste gry bez pomocy człowieka czy nawet zostania mistrzem świata w Go i tworzenia własnych skomplikowanych strategii. Jest to niestety najbardziej wymagający czasu jak i mocy obliczeniowej sposób nauki.
Dwa najpopularniejsze rodzaje problemów to klasyfikacja oraz regresja.
Nasze wejściowe dane łączone są w model na podstawie którego jest określane czy spełnione są odpowiednie warunki czy też nie. Wynik z największym prawdopodobieństwem zostanie nam zwrócony. Na przykład rozpoznawanie ręcznie narysowanych cyfr, w tym przypadku mamy dataset w którym są próbki z zdjęciami narysowanych cyfr oraz etykiety dla każdego z nich. Podajemy nasze dane (zdjęcie) oraz trenujemy sieć używając naszego datasetu i otrzymujemy wynik.
Działa to tak jak wyżej, lecz z jedną różnicą, a mianowicie tutaj otrzymujemy ciąg wyników/tablice z wynikami, a nie tylko jeden wynik. Przykładem problemu regresji może być przewidywanie ceny bitcoina w zależności od jego aktualnego kursu oraz w oparciu o dane z poprzednich miesięcy.
Podsumowując, Machine Learning ma wielki potencjał niemal w każdej dziedzinie, myślę, że ten wstęp powinien przybliżyć Ci jak mniej więcej to działa.
Źródła:
Teraz może coś o naszym małym zespole, o którym raczej też należy wspomnieć. Głównym pomysłodawcą i programistą gry jestem ja, Denis. Pomysł na DevTycoon miałem już na początku 2017 roku, lecz brakowało mi umiejętności kodowania oraz modelowania. Jednym z pierwszych pomagierów został mój najlepszy internetowy przyjaciel, Jakub, który odpowiedzialny jest za całe otoczenie w świecie gry oraz trochę za działanie gry. Jako że to on najwięcej czasu poświęcił w granie gry na jego youtubowym kanale, został on moim prywatnym doradcą w sprawie responsywności interfejsu oraz ogólnie całego gameplay’u. Z tego miejsca mogę też wspomnieć o dwóch innych osobach, mianowicie o Staśku oraz o Wiktorze . Stasiek bardzo dużo pomaga przy zaawansowanych modelach , to dzięki niemu Auta mają wnętrze, a komputery to nie klocki, niczym z Minecraft’a. Wiktor przygotował dla Was stronę , którą będzie z czasem rozbudowywał pod Betę oraz Alphę. I teraz najważniejsza grupa, czyli Testerzy przedpremierowi. Bardzo chciałbym podziękować Root’owi oraz iLiquid’owi za udział w testach, które pomogły nam wyłapać głupie, aczkolwiek ciężko wykrywalne błędy. Ogólnie, gdyby nie te wszystkie osoby, gra by najprawdopodobniej nie powstała, bo by mnie to autentycznie przerosło.
Teraz chyba odpowiedź na pytanie, na którą wszyscy czekają. Czym DevTycoon będzie różniło się od innych tego typu gierek? W sumie, to niczym, oprócz tego, że chcemy zapożyczyć wszystkie udane pomysły, dodać do nich troszkę oryginalności i wszystkie włożyć pod jedną maskę. Np. w Simsach da się wchodzić w zaawansowaną interakcję z simem, u nas będzie podobnie, lecz trochę mniej zaawansowanie i bardziej pod temat programowania. Teraz zostają wszystkie inne gierki: Mad Games Tycoon, Game Dev Tycoon, StartUp Company oraz Software Inc. Każda z tych gier jest poświęcona innemu tematowi w dziedzinie programowania. W jednych możesz programować gry, w drugich antywirusy, a w jeszcze innych możesz robić nawet strony internetowe. Co by się stało, gdyby to wszystko połączyć w jedną całość? Podjęliśmy się zadania, aby to sprawdzić.
Co dalej z grą? Nie wiemy. Jeśli Demo Technologiczne wyjdzie znakomicie , a opinie będą w miarę dobre, to będziemy starać się rozwijać grę jak najlepiej, a w przyszłości wydać ją nawet na steamie. O fundingu na razie nie myślimy. Być może założymy Patronite, aby najbardziej zainteresowane osoby mogły nas wspierać finansowo, ale bardzo staramy się robić wszystko samemu .
// Krótki gameplay z gry zostanie dodany, kiedy będzie on gotowy.
Link do pobrania znajduje się na stronie: https://devtycoon.com/
Podsumowując: Wszelkie pomysły i uwagi na temat gry prosimy kierować na nasz adres Email: [email protected] , na wszystkie postaramy się odpowiedzieć.
Trochę screenów:
]]>
Bardzo popularnym, często powielanym błędem podczas deploymentu na Windowsa jest czekanie na błędy i wrzucanie bibliotek „na pałę” do folderu z binarką aplikacji, jest to pracochłonne, i działa tylko w niektórych przypadkach:
Trochę lepszym sposobem jest używanie dependency walker który, skanuje plik exe w poszukiwaniu zależności, i wyświetla brakujące biblioteki.
Zacznę od aplikacji
nieużywających komponentów Qt Quick
, do tego celu użyjemy narzędzia
windeployqt
, znajduje się ono w katalogu Qt, podkatalogu wersji, podkatalogu
mingw
, i wreszcie w folderze
bin
w moim przypadku jest to
C:/Qt/5.9/mingw53_32/bin
.
Najczęściej popełnianym błędem, który i ja popełniłem jest użycie złej wersji narzędzia
windeployqt
, dlatego wcześniej warto się upewnić jakiej wersji Qt użyliśmy do budowy aplikacji.
Teraz czas uruchomić
cmd
i wywołać
windeployqt
wraz z odpowiednimi argumentami, w przypadku prostej aplikacji będzie to
Narzędzie automatycznie wykryje potrzebne biblioteki, znajdzie je i dorzuci do katalogu z naszym plikiem exe.
W przypadku aplikacji Qt Quick, należy dodatkowo załączyć adres projektu, wynika to z tego, że aplikacja wymaga dynamicznie zaczepionych plików qml od komponentów które zostały użyte, w tym celu używamy windeployqt zgodnie ze składnią:
Tu warto wspomnieć, że częstym
błędem
jest mylenie folderu projektu, z folderem
qml
który znajduje się obok folderu
bin
.
Przykładowe użycie:
Po więcej argumentów w razie potrzeby należy wywołać:
Polecam też zajrzeć na http://doc.qt.io/qt-5/windows-deployment.html .
]]>
Nazywam się Adam Jachowicz , jestem z Nastoletnimi od ich początków (dołączyłem do grupy 3 maja 2015, mogę się już chyba nazywać weteranem?). Większość z Was kojarzy mnie (jeżeli w ogóle, bo apogeum aktywności osiągnąłem bardzo dawno ) z bycia głównym redaktorem bloga http://programistycznepieklo.pl . No i jak możecie się domyślać – to mnie przypadło podsumowanie wyników ankiety. Sama przyjemność!
Przeciętny członek NP jest mężczyzną (cóż za zdziwienie) , ma 15-17 lat. Jego głównym obszarem zainteresowań jest desktop , zwykle ma też jakieś doświadczenie z webdevelopmentem . Zna też C++ , oprócz tego zwykle PHP i JS , rzadziej Javę , SQL i C# . Nigdy nie pracował ani nie wykonywał żadnego zlecenia. Ocenia swoją satysfakcję z programowania na 8.5 w skali od 1 do 10. Jeżeli przyszedłeś tylko po to co obiecywał tytuł – to tyle, możesz już zamknąć tę stronę.
Jeżeli jednak chcesz zobaczyć coś więcej…
Czas odpowiedzieć na pierwsze pytanie jakie może nasuwać się uważnemu czytelnikowi – skąd wiem, że większość osób będących członkami NP nie wiedziało o ankiecie? Albo konkretniej, że większość osób jej nie wypełniła ?
Ankietę (na dzień 01.04.2017) wypełniło 388 osób (ale pod uwagę wziąłem tylko 372, dalej okaże się czemu) – to mało jak na 5754 członków… Tak na dobrą sprawę to bardzo mało – dokładnie 6.47% grupy. Można by dyskutować, czy taka próbka nadaje się do analizy, ale to jedyne do czego mam dostęp, więc… ¯\_(ツ)_/¯
Myślę że nie będzie tu niespodzianki, więc nie ma się co rozpisywać:
W sumie może i jest co napisać na ten temat… Oczywiście no offence i te sprawy, ale… kobiety wyglądają trochę jak błąd statystyczny ( ͡° ͜ʖ ͡°)
Tu już na szczęście robi się trochę ciekawiej . Znaczy, no, kobiety nadal wyglądają jak błąd statystyczny ( ͡° ͜ʖ ͡°) ale widać tu już jakieś ciekawsze powiązania. Przykładowo, kształt wykresu powinien być dla niektórych znajomy… Tak, to bardzo ładna krzywa normalna. Co prawda przesunięta nieco w stronę młodszych, ale nadal normalna.
Krzywa normalna (dzwonowata) – jedna z ważniejszych krzywych w statystyce ze względu na jej częste występowanie w naturze (m. in. krzywa wykresu obrazującego rozkład wartości IQ czy wzrostu lub nawet błędu pomiarowego). Posiada pewne ciekawe właściwości, ale ich tłumaczenie nie wchodzi jednak w zakres tego artykułu.
Jest jeszcze jedna rzecz która powinna się rzucić w oczy po analizie etykiet słupków… Jeżeli to zauważyłeś – moje osobiste gratulacje . Jeżeli nadal tego nie widzisz – istnieje słupek 20 jak i „>20”, ale pary „13” i „<13” już nie widać . Jak to się stało? No, nie chcę przywoływać nazwisk i imion, ale pewien z administratorów którego imię zaczyna się na A i kończy na lbert zapomniał dodać takiej opcji w ankiecie ( ͡° ͜ʖ ͡°) No nic, na potrzeby tego artykułu musimy założyć że każde „<13” jest tak naprawdę odpowiednikiem „<14”.
W ankiecie trzeba było wybrać technologię na dwa sposoby – podać swoją główną oraz wszystkie znane technologie. Najpierw zajmiemy się tą główną:
Wartości procentowe odnoszą się do ogółu respondentów.
Co tu widzimy? Połowa członków NP jako target swoich programów wybiera głównie desktop, a 1/3 pisze stronki. Także nic zaskakującego,
w końcu C++ i JS jest jednym z najbardziej
ooops, jeszcze tu nie dotarliśmy
Pod
Innymi
kryją się odpowiedzi takie jak:
konsolka,
Inne, (???)
asm, ale reverse engineering różnych technologii,
Teamspeak.
Skoro mamy dane dotyczące wieku jak i dane dotyczące technologii, możemy je skorelować – to dwa z ciekawszych wykresów:
Jak widać – wraz z wiekem więcej osób dryfuje z desktopa do innych technologii. Czemu tak się dzieje? Ano dlatego, że im młodszy jesteś, tym większe prawdopodobieństwo że dopiero się uczysz. A od czego zwykle zaczynają młodzi adepci programowania? Od C++. Potem dopiero zaczyna się odkrywanie technologii która jest ci bliska – najpierw zawsze jest ten proces nauki. Nie dziwi też to że nie przechodzą do webdevu – tak właściwie to następuje tu rotacja w obie strony, ponieważ drugim zestawem języków od którego często zaczynamy przygodę z programowaniem jest HTML, CSS i JS. A czemu wzrósł udział mobilek i mikrokontrolerów, i to akurat względem desktopa? Proste – programując na desktop, łatwiej jest się potem przerzucić na którąś z tych technologii niż na webdev.
Ten trend łatwiej będzie zauważyć na tym wykresie:
Wartości procentowe obu słupków przy wieku nie sumują się do 100%, ponieważ są to wartości żywcem wyciągnięte z takiego jak powyżej wykresu kołowego – ważne są tu różnice w wysokości słupków, które wraz z wzrastającym wiekiem utrzymują w miarę stały trend malejący. Błędy, małe skoki w górę czy wielki skok nie pasujący do tezy przy grupie 20 czy >20 jest spowodowany wielkością próbki – tak jak mówiłem, jest trochę za mała by wyciągać z niej dokładne wnioski bezpośrednio, jednak pewnych rzeczy jako człowiek z doświadczeniem w takich rzeczach mogę się domyślić na podstawie nawet i takich danych
Wyniki drugiego związanego z technologią pytania nie powinny być zaskoczeniem po przeczytaniu wcześniejszego tekstu:
Tutaj wartości nadal nie sumują się do 100%, ale tu powód jest prostszy – pytanie było pytaniem wielokrotnego wyboru i dotyczyło wszystkich znanych technologii. Ogólnie to nic ciekawego, ale mogę przywołać to co tym razem znalazło się pod pojęciem Inne :
Skryptowanie gier,
LUA (Multi Theft Auto: San Andreas),
dużo, (???)
Serwery, bazy danych,
Games,
Teamspeak,
STM32.
Po technologii czas na to co dla mnie osobiście jest najciekawszym zbiorem wektorów danych…
Zanim pokażę pierwszy wykres związany z tym działem muszę was ostrzec – praktycznie każdy wykres tutaj może dla was wyglądać tak samo. Muszę was jednak zapewnić że nie są takie same – wrażenie takie może się wziąć z tego, że próbują one pokazać jak najwięcej danych w jak najmniejszej/najefektywniejszej/najczytelniejszej postaci. No i kolorki są podobne.
Nie przedłużając, pierwszy kolos:
Może na początku wytłumaczę co dokładnie ten wykres tak właściwie przedstawia. Po lewej mamy języki. Na dole jest liczbowa reprezentacja zaawansowania – wygodniej było się z tym obchodzić w kodzie, myślę że jest też bardziej przejrzyście na wykresach. Cyfry oznaczają:
W ten sposób mogę traktować zaawansowanie w języku jako ciągłe, liniowe pasmo a nie jako zbiór 6 stopni – wygodniej
Wykres na górze przedstawia procent wszystkich respondentów mających dane zaawansowanie w danym języku. Przykładowo: 83 procent wszystkich respondentów nie zna Assemblera, 11 procent w jakiś sposób się z nim zetknęło ale tylko około 6 procent może się pochwalić pełniejszą wiedzą.
Możecie zauważyć, że na wykresie procenty dla Assemblera sumują się nie do 100, a do 101 procent – powodem są błędy zaokrągleń, niestety :/
Oprócz tego wykres posortowany jest tak, że język z największym średnim poziomem znania jest na górze, a ten z najmniejszym – na dole. Dzięki temu bardzo łatwo jest zobaczyć jakie języki są najpopularniejsze wśród członków naszej grupy – są to C++ , PHP , JS , SQL , C# , Java , Python i C . Trzeba jednak nadmienić że C++ ma nad nimi znaczącą przewagę, jak zresztą widać na wykresie. Można powiedzieć że języki od Perla w dół są językami nieznanymi na NP.
Osobiście mi trochę smutno że języki nowatorskie czy niestandardowe (Haskell, Go, Clojure, Scala, Groovy) lub specjalistyczne (Prolog, R) są pomijane przez członków NP – wiadomo, rzadko przyjdzie pisać większy projekt z ich udziałem ale ich znajomość może wiele rzeczy wyjaśnić, pokazać inne podejście do programowania. Ale cóż, co ja mogę ¯\_(ツ)_/¯
Co ciekawe, mamy raczej skromnych programistów, mało osób zaznaczało w ankiecie ekspercki poziom znania języka. Nawet w przypadku C++ było to tylko 3 % ankietowanych. Kochani, więcej pewności siebie ^^
Następny wykres (Wyk. 8) może być ciekawszy ze względu na to, że pokazuje on średnią znajomość danego języka wyrażoną w procentach w danej grupie wiekowej – oczywiste jest że wartości nie mogą się tu sumować do 100 %.
Na tym wykresie bardzo ciekawie widać jedną rzecz – to, że z wiekiem umiemy coraz więcej. Niby taki banał, a tak ładnie zwizualizowany
Ciekawym ewenementem są członkowie w wieku 20 i >20 lat – zacznijmy może od dwudziestek.
Z danych jednoznacznie wynika… że są głupsi. Tak naprawdę to jedyny rocznik który nie podąża za trendem wzrostu wiedzy wraz z wiekiem. Czym może być to spowodowane? Właściwie to nie mam bladego pojęcia. W następnym wykresie ten spadek będzie widoczny jeszcze bardziej.
Chociaż właściwie to jest jedna rzecz w której trzymają się trendu – w językach, które zwykle są ignorowane przez programistów ;-; Ciekawe, prawda? Popularność języków wśród danych grup wiekowych także będzie przedmiotem jednego z następnych wykresów, więc będzie można ładnie zobaczyć tę zależność.
Osoby posiadające ponad 20 lat na karku są za to z niektórymi językami aż za bardzo do przodu (Pascal, Swift). Pierwszy z nich łatwo wytumaczyć – starsze roczniki często w szkołach uczyły się Pascala czy Delphi więc konsekwencją tej nauki musi być wyższy poziom poznania języka. Swift za to nie jest już taki banalny do wytłumaczenia – co tu się stało?
Na wynik wpłynęło to, że >20 to większa zasięgowo grupa niż na przykład takie 14 . Wśród osób zaznaczających tą odpowiedź mogły znajdować się zarówno dwudziestojedno jak i dwudziestopięciolatkowie. A jeżeli zakładamy że z wiekiem wiedza rośnie, to w wynikach grupy >20 nie ma nic dziwnego.
Czternastolatków przywołałem tu nie bez przyczyny – z jakiegoś dziwnego powodu są za bardzo zaawansowani w C# jak na swój wiek… Ma ktoś jakiś pomysł jak to wyjaśnić? Komentarze są wasze .
Co ciekawego jeszcze widzimy? Dziewiętnastolatkowie wychodzą poza szereg jeżeli chodzi o języki inne niż C++. To z kolei może oznaczać dwie rzeczy:
Pamiętacie co pisałem na początku? Z 388 odpowiedzi wziąłem pod uwagę tylko 372. Dlaczego? Właśnie z tego powodu – dane od trolli bardzo zakrzywiały różne korelacje/wyniki.
Myślę że można już przejść do kolejnego, obiecanego wykresu:
Cyferki mało tutaj znaczą – ten procent to średnia z kolumn z poprzedniego (Wyk. 8) wykresu. Ważne są zależności i trendy, które tutaj widać bardzo wyraźnie.
Pierwsze co się rzuca w oczy – wyraźny ubytek przy słupku opisanym 20 . Wcześniej już o tym pisałem – na tym wykresie można to zauważyć dokładniej. Jakieś pomysły? Znaczy, to zawsze może być błąd statystyczny, bo przy grupie badanych równej 13 osobom… No ale o małej wielkości próbki już pisałem…
Skok u dziewiętnastolatków również widać tutaj doskonale, przyczyny również opisałem wcześniej. Ten wykres służy właściwie podsumowaniu danych z tamtej mapy ciepła, nic szczególnego za sobą nie niesie.
W tym momencie podczas pisania tego artykułu się zadumałem. Czy ktokolwiek dojdzie do tego momentu? – myślałem. Licznik słów przekręcił się na magiczną liczbę 1720. Była we mnie nutka zwątpienia, ale także i ciekawości. Jeb^CChędożyć to! – powiedziałem w myślach. I zacząłem pisać dalej, zostawiając pytania o sens mojej egzystencji gdzieś na później.
Kolejny wykres również jest mapą ciepła. Znowu wartości w rzędach nie sumują się do 100%. Czemu więc? Wykres przedstawia procentową znajomość danego języka w danych grupach wiekowych.
W tym wykresie ważne jest, żeby przedstawić założenia na których opierałem się żeby zaliczyć dany język do „znanego” przez respondenta czy też nie. Problem w tego typu wykresach jest taki, że nie da się jednoznacznie, obiektywnie określić czy dany język jest przez kogoś znany. Bo jak określić znajomość?
Teoretycznie mogę nie programować w danym języku, ale mimo to znać go, chociażby tylko z nazwy. Chyba nie będzie sporów, gdy powiem że znam taki język? Niestety w tej ankiecie nie miałem szansy wdrożyć takiego podejścia przez sposób zadania pytania o języki – ewidentnie kładło ono nacisk na poziom poznania języka, a nie na jego znajomość .
Musiałem więc posłużyć się uproszczoną metryką znajomości i założyć, że każdy kto zaznaczył przy danym języku poziom zaawansowania przynajmniej 1 ( Nowicjusz ) zna ten język, a reszta która zaznaczyła 0 tego języka nie zna .
Przechodząc do analizy… Jest taki język który zna każdy. Niezależnie od grupy wiekowej. Małe odchyłki w liczbach właściwie nic nie znaczą, mapa ciepła mówi sama za siebie – pasek przypisany C++ jest w całości gorący .
Nasz poprzedni fenomen, dziewiętnastolatkowie, nadal wyróżniają się w tabeli, głównie za sprawą języków stricte webowych – JS’a i PHP . Co ciekawe – kosztem Javy .
Starsi, grupa >20 jak można było przewidzieć cechuje się najszerszą znajomością języków. Można też zobaczyć że moja hipoteza o Pascalu potwierdza się – na mapie widać, że starsze roczniki umieją go w znacznie większym procencie osób niż inne.
Dwudziestolatkowie mają z kolei problemy z… C#’em . W sumie oni mają ze wszystkim problemy (jak mogliśmy zobaczyć na wcześniejszych wykresach)… Ale to i tak dziwne jak na ogół. Za to znają Assemblera i Haskella na najlepszym poziomie ze wszystkich badanych grup wiekowych – zawsze coś ( ͡° ͜ʖ ͡°)
Ostatni już wykres (Wyk. 11) pokazuje rozkład procentowy znajomości danego języka w zależności od wybranej technologii głównej.
Skróty na dole pochodzą od nazw technologii:
Widać tu pewne silne trendy – przykładowo, 79% wybierających AVR/ARM zna C. Nie powinno być tu zaskoczenia, tak samo jak przy fakcie posiadania największej procentowej znajomości C++ wśród wszystkich grup technologicznych . Znają też dobrze Pythona , PHP i JS’a . Co do tego PHP i JS’a – podejrzewam że wynika to z błędu statystycznego, mała grupa badanych. Pythona dałoby się wytłumaczyć tym, że to popularny język skryptowy, jednak nadal myślę że nie usprawiedliwia to jego popularności w tym zestawieniu.
Co ciekawe, Webowcy znają Pascala niż inne grupy technologiczne – kto spodziewał się takiego wyniku? Mimo to, uzasadnienie istnieje – w szkołach obok Pascala jako języka stricte desktopowego nauczany jest też HTML razem z elementami CSSa i (czasem bo czasem, ale jednak) JSa. Wiadomo, że jakaś część uczniów wybierze Web jako swoją główną technologię, druga część wybierze Desktop. A jak mogliśmy zobaczyć na wykresach 3, 4.1 i 4.2 istnieje tendencja do wyrównywania się ilości osób mających za swoje główne technologie Web i Desktop wraz z końcem procesu kształcenia szkolnego. Więc zagadka rozwikłana
Wysoka znajomość Javy wśród Mobilnych chyba nie dziwi, tak jak najmniejsza ze wszystkich grup znajomość C++’a . Ciekawi najniższa wśród wszystkich grup znajomość Objective-C . Tak, wiem że teraz wszyscy w Swift’cie piszą, ale jednak – w ankiecie było pytanie o znajomość, a nie o używanie danego języka. W porównaniu do innych grup wygląda to słabiutko… No, ale za to wspomniany już wyżej Swift znają najlepiej ze wszystkich.
Trochę dziwi taka monotematyczność wśród Desktopowców – można to jednak tłumaczyć tym, że wśród desktopowców dużą przewagę mają młodsi, dopiero uczący się adepci programowania. Java czy C# nie są tu znane tak bardzo jak porównywalne języki w innych grupach, za to C++ trzyma się na stałym, wysokim poziomie znajomości wśród reprezentantów grupy Desktopowej.
Post zrobił się nieco długi, powstawał też masę czasu – niestety, ale muszę go podzielić na dwie części. W następnej – dalsza część ankiety, czyli satysfakcja badanych kontra różne czynniki oraz praca i jej szczegóły takie jak typ umowy czy średnia płaca za godzinę. Będzie ciekawie! A do czasu pojawienia się następnej części zapraszam serdecznie do komentowania i zadawania pytań – na każde postaram się odpowiedzieć
A – no i w następnej części udostępnię kod generujący wykresy (jest konfigurowalny) oraz dane na których pracuję – stay tuned!
]]>
Niektóre z tytułów wręcz są popularne ze swojej prymitywnej S.I.:
Policjanci w GTA.
Wbrew popularnej opinii sztuczna inteligencja policjantów w San Andreas nie jest aż taka prosta, jednak można by było ją trochę doszlifować. W tym artykule rozpoczniemy tworzenie podstawowego zmysłu dla przeciwników – zmysłu wzroku i wprowadzimy sobie prosty interfejs do debuggingu.
Zaczniemy od kodu finalnego z mojego poprzedniego artykułu: Jak programować gry? Najważniejsze elementy w grach 2D. Kod źródłowy z tamtego artykułu znajdziecie tutaj: GitHub
Rozpoczniemy od stworzenia klasy gracza oraz klasy przeciwnika, które nazwiemy odpowiednio
CPlayer
oraz
CEnemy
. Następnie stworzymy prosty kontroler dla obydwu, a dodatkowo w kontrolerze gracza dodamy możliwość poruszania się.
Podobny schemat realizowałem już w filmie o tworzeniu bombermana, jednak przewiduję, że prawdopodobnie go nie obejrzałeś (bo komu chciałoby się oglądać w całości film o tworzeniu gry trwający 2 godziny). Z tego względu jeszcze raz wytłumaczę, co zrobiliśmy:
I) Utworzyliśmy klasę gracza i jego kontrolera:
CPlayer
– klasa gracza
CPlayerController
– klasa kontrolera gracza zawierająca sterowanie
Opisałem kod tak, by było dokładnie widać co krok po kroku trzeba wykonać.
Plik nagłówkowy
Player.hpp
:
Plik źródłowy
Player.cpp
:
II) Utworzyliśmy klasę wroga i jego kontrolera. Póki co nasze A.I. zostawimy w spokoju. Zajmiemy się nim w następnej części tego artykułu.
CEnemy
– klasa wroga
CEnemyAIController
– klasa kontrolera S.I. wroga
Plik nagłówkowy
Enemy.hpp
:
Plik źródłowy
Enemy.cpp
:
III) Wczytaliśmy potrzebne tekstury przy uruchamianiu gry:
IV) Stworzyliśmy gracza i jednego wroga, a następnie dodaliśmy ich do poziomu:
Sztuczna inteligencja w grach jest oparta o różne zmysły np.: zmysł wzroku, zmysł słuchu czy nawet czasem zmysł węchu. Najprostszym do zaimplementowania jest zmysł wzroku, który jak zapewne łatwo jest się domyślić, będzie odpowiadał za widzenie. Będzie on działał w ten sposób, że będzie sprawdzał, czy jest coś interesującego w zasięgu wzroku oraz w obszarze widoku (kąt widzenia zrobimy łatwy do dostosowania) i jeśli coś wykryje, to zwróci to w wyniku.
Na początku, jako że wszystkie zmysły będą miały kilka wspólnych metod i własności, utworzymy sobie klasę bazową. Nazwałem ją IAISense (w swoich projektach przedrostek „I” dodaje do klas bazowych, dostarczających swego rodzaju interfejs do późniejszego rozbudowywania, jednak takich, które nie mogą być bezpośrednio używane). Do implementacji stworzymy sobie osobne pliki –
AISense.hpp
i
AISense.cpp
.
Teraz, zanim przejdziemy do kodu, zastanówmy się, co będzie musiał mieć każdy zmysł. Po chwili rozmyślań doszedłem do wniosku, że:
Będąc świadomym tego, do czego dążymy, utworzyłem następującą klasę bazową:
Warto bardzo dokładnie przeanalizować komentarze, szczególnie ten przy metodzie QueryActors, bo jest to kluczowa metoda tej klasy. Teraz do zaimplementowania został nam tak naprawdę tylko konstruktor (zwróć uwagę, że
QueryActors
jest metodą czysto wirtualną,
GetSensedActors
jest zdefiniowana jako
inline
).
Kod konstruktora jest bardzo prosty:
Pokusiłem się tutaj o referencje na klasę
IPawn
(lub bazową) właściciela. Takie rozwiązanie wymusza podanie go (gdybyśmy użyli wskaźników, ktoś mógłby podać np. pusty wskaźnik), przez co reszta kodu, która się do niego odnosi, zadziała poprawnie.
Następnym etapem będzie stworzenie zmysłu wzroku. Aby mieć jakiś wgląd w sposób jego działania, spójrz na poniższy obraz:
Zatem mamy do stworzenia kolejną klasę, która będzie zawierała kąt i zasięg widzenia oraz implementacje metody
QueryActors
, która sprawdzi, czy aktorzy są polu widzenia. Zdecydowałem, że umieszczę tę klasę również w plikach
AISense.hpp
i
AISense.cpp
by nasz projekt nie miał za chwile 40 plików.
Tak oto wygląda ta klasa:
Tym razem do zaimplementowania mamy konstruktor oraz trzy metody. Może zajmijmy się najpierw konstruktorem:
Nie wrzuciłem ustawienia
m_sightDistance
i
m_sightAngle
do listy inicjalizacyjnej, ze względu na to, że mamy od tego odpowiednie funkcje, które zapobiegają wprowadzeniu nieodpowiednich danych.
Teraz spójrzmy dalej do metod
SetSightDistance
i
SetSightAngle
. Odpowiednie środki bezpieczeństwa są tutaj wymagane. Nie chcemy przecież mieć ujemnego zasięgu lub kąta widzenia > 180 stopni. Warto również zauważyć, że podane funkcje zwracają
true
,
jeśli poprawnie ustawiono wartość a
false
,
jeśli wartość była niepoprawna. Nie będziemy póki co z tego korzystać, ale warto mieć coś takiego zaimplementowanego – może niedługo się przyda
Przyszedł czas na kluczowy moment. Teraz zajmiemy się całą logiką zmysłu. Pomyślmy, co powinien on krok po kroku zrobić:
Problemem jednak jest to, że w poprzednim artykule nie stworzyliśmy sobie metody, dzięki której zyskamy dostęp do listy aktorów ze sceny. Dlatego właśnie potrzebna jest nam poniższa funkcja, którą dodałem do klasy
CLevel
:
Na tych założeniach zbudowałem taką funkcję. Polecam przeanalizować po kolei każdy etap jej działania, gdyż właśnie jesteśmy w punkcie kulminacyjnym tego artykułu.
Uff… już najtrudniejsze za nami. Teraz możemy dodać już zmysł wzroku do wroga.
CSightSense m_sightSense; // Zmysl wzroku wroga
Należy również pamiętać o prawidłowym wywołaniu konstruktora zmysłu z poziomu listy inicjalizacyjnej. Następnie, aby nasz zmysł „działał” musimy go ciągle uaktualniać. Do tego posłuży nam metoda
IActor::Update
, którą sobie przeładujemy. Musimy jednak pamiętać, że metoda ta również ma swoją implementację w klasie bazowej
IPawn
, więc musimy umieścić też odwołanie do implementacji bazowej:
Co nam jednak z tego, że tak się narobiliśmy, a wciąż nie widzimy efektów? Właśnie dlatego teraz zaimplementujemy…
Wyszukiwanie błędów w grach jest bardzo uciążliwe, jeśli przed sobą mamy same cyferki i nic konkretnego. Dlatego właśnie wiele gier dodaje sobie prosty panel debugowania „in-game”. Tym właśnie się teraz zajmiemy.
Na samym początku już wiemy, że potrzebna będzie nam funkcja generująca kształt wycinka koła. Jako że SFML sam jej nie dostarcza, postanowiłem napisać ją sam. Stworzyłem więc plik z deklaracją
SFMLShapes.hpp
oraz plik źródłowy
SFMLShapes.cpp
. Możliwe, że w przyszłości potrzebne będzie nam więcej własnych kształtów i wtedy umieścimy ich generowanie również w tych plikach.
Nazwałem ten kształt Pie (odnosi się do angielskiej nazwy ciasta, ponieważ nasz kształt to jakby wycinek ciasta ).
Powyższa funkcja nie jest w pełni funkcjonalna, jednak wystarcza do podstawowych zastosowań. Jej mankamentem jest to, że używa
sf::ConvexShape
(convex – wypukły). Może to spowodować niechciany efekt przy wyświetlaniu wycinka o kącie > 90 stopni. Jeśli ktoś ma na tyle ochoty, żeby się z tym bawić, to polecam do tego użyć
sf::VertexArray
.
Teraz już możemy wykorzystać ten generator. Pamiętamy jeszcze klasę
IAISense
? Każdy zmysł będzie mógł się popisać jakimś fajnym symbolem przy debuggingu, dlatego też utworzymy metodę dla tej klasy, która będzie rysowała takie symbole na ekranie.
Metoda ta, nie jest metodą czysto wirtualną, bo zmysł ma mieć możliwość wyświetlenia debug info, ale nie jest do tego zmuszany.
Teraz czym do diabła jest to
static bool DebugMode;
?
Dobrym pomysłem jest posiadanie jakiegoś przełącznika, którym będziemy sterowali, by albo włączyć tryb testowy, albo go wyłączyć. Statyczna zmienna
DebugMode
jest właśnie takim przełącznikiem. Oczywiście pamiętamy o tym, że taką zmienną statyczną trzeba też zainicjalizować. Najprościej będzie zrobić to na początku pliku źródłowego:
bool IAISense::DebugMode = true;
Mamy już bazę, to teraz trzeba zaimplementować wyświetlanie informacji trybu testowego dla zmysłu wzroku. Zrobiłem to tak:
Weźmy głęboki oddech… w tym miejscu artykuł ten możemy zakończyć jedną linijką, którą dodamy, by nasz debug info mógł się w ogóle wyświetlić. Jak zapewne się już domyśliłeś, umieścimy ją w metodzie wyświetlającej wroga na ekranie.
m_sightSense.DrawDebug();
W tym artykule zbudowaliśmy sobie podstawę pod dalsze rozwijanie modułu sztucznej inteligencji. Dodaliśmy również możliwość wyświetlania podstawowych informacji debuggingu. Kod z tego artykułu znajdziesz tutaj: GitHub . Teksturki można pobrać tutaj: Mega .
]]>
Uwaga, aby artykuł nie miał setek stron, pominąłem dogłębne tłumaczenie kodu Javy/PHP/SQL. Co nie znaczy, że jeśli jesteś zaciekawionym tematem to nic nie zrozumiesz. Postarał się w miarę możliwości dobrze okomentować kod. W każdym razie czujcie się ostrzeżeni!
Z nielegalnym oprogramowaniem spotykamy się na co dzień – w Polsce jest ono dosyć popularne . Tworząc aplikację, którą chcemy sprzedać komuś, chcielibyśmy zadbać o to, żeby inni nie mieli do niej dostępu. Dlatego programiści starają się wymyślać coraz to nowsze zabezpieczenia przeciw piratom. Chciałbym pokazać jedną z prostych metod zabezpieczenia aplikacji.
Typowe zabezpieczenia:
Do każdego z tych punktów można dopisać jeszcze jedną wadę:
Żadne zabezpieczenie nie jest całkiem bezpieczne
Żadne z tych zabezpieczeń nie odeprze jednego: zmiany kodu programu. Dlatego warto jest korzystać z różnych zabezpieczeń, które modyfikują nam nazwy zmiennych/funkcji w kodzie, aby potem kod był trudny do rozczytania po dekompilacji.
Przed zastosowaniem zabezpieczenia warto przemyśleć, które warto wybrać. Jak każdy wie, stały wymóg połączenia internetowego może być denerwujący dla użytkownika. Jeżeli nie robicie aplikacji, która operuje na połączeniu internetowym, to nie zawsze warto wybierać to zabezpieczenie. Na dzień dzisiejszy najlepszym sposobem wydawać się może jednorazowa aktywacja – ma ją np. Windows, Steam. W tym artykule chciałbym zaprezentować mój sposób na system aktywacji, który przedstawiłem jako nr. 3 w liście wyżej; czyli – aktywacja urządzenia online.
Dlaczego akurat to wybrałem? Gdy myślałem o zastosowaniu mojej aplikacji, stwierdziłem, że zależy mi, aby nie była ona wykorzystywana przez jedną osobę na kilku urządzeniach. Dlatego aktywacja urządzenia wydaje się być rozsądnym wyborem.
Jak to wygląda?
Przy aktywacji urządzenia pierwszą rzeczą, jaka jest nam potrzebna, to pobranie jakiegoś identyfikatora urządzenia. I to jest drobny problem – co można wziąć? W większości przypadków znajdziecie na internecie wykorzystanie adresu MAC karty sieciowej, ale to rozwiązanie jest dosyć słabe – ponieważ w przypadku laptopów jest możliwość, iż aplikacja była instalowana gdy interfejs sieciowy WLAN był włączony, a potem wyłączony. Adres MAC w aplikacji zostanie zwrócony inny i aplikacja
przestanie
działać
. Dlatego polecam np. wykorzystać identyfikator BIOS’u. W Windowsie pobiera się go wpisując komendę w konsoli:
wmic bios get serialnumber
, w przypadku Linuxa sprawa jest nieco trudniejsza, ale i tak są pewne rozwiązania.
Okej – mamy już identyfikator sprzętowy – teraz przydałoby się go jakoś wysłać do serwera i odebrać plik weryfikacyjny. Tak to będzie wyglądać:
Ogólny schemat pierwszej aktywacji prezentuje się tak:
Schemat będzie się nieco
różnić
w przypadku, gdy klient aktywuje drugi raz aplikację na tym samym urządzeniu. Wtedy gdy serwer wykryje że klucz jest już użyty (czyli
used_num>=1
) to będzie sprawdzać tabelę
activations
i będzie próbował znaleźć
hardware_id
taki, jaki wysłał klient. Jeśli znajdzie takowy w tabeli, to oznacza, że klucz jest poprawny i odsyła klientowi klucz weryfikacyjny z kolumny
response_key
.
Przejdźmy do implementacji. Potrzebny nam serwer + baza danych na tym serwerze. Ja wybrałem bazę danych MySQL. Stworzyłem bazę danych o nazwie
Activation
, a w środku dwie tabele:
activation_keys
– w tej tabeli zawarte są wszystkie zaszyfrowane klucze programu i ich liczba użyć
CREATE TABLE activation_keys (id int PRIMARY KEY NOT NULL AUTO_INCREMENT, activation_key varchar(72) NOT NULL, used_num int);
activations
– w tej tabeli zwarte są wszystkie aktywacje, identyfikator sprzętowy i identyfikator klucza z tabeli activation_keys
CREATE TABLE activations (id int PRIMARY KEY NOT NULL AUTO_INCREMENT, id_key int NOT NULL, hardware_id varchar(64), response_key TEXT);
Identyfikator urządzenia będziemy pobierać, korzystając z wyżej wymienionego kodu + hashowania SHA-256.
Klucze będziemy szyfrować algorytmem BCrypt , który generuje maksymalnie 72-znakowy kod. Na obrazku obok jest pokazany schemat BCrypt. Jest to bardzo zalecane , gdyż mając atak hakerów na bazę danych, będą oni w stanie sprawdzić wszystkie dostępne klucze . Ogólnie, BCrypt zamienia klucz do formy zhashowanej. Ten proces trudno odwrócić, tzn.posiadając hash, trudno odnaleźć taki klucz, który po zhashowaniu będzie mieć taką samą formę jak zhashowany. BCrypt często wykorzystuje się do zapisywania haseł użytkowników na wielu stronach internetowych. Klient wysyła do serwera hasło w formie zahashowanej , a następnie jest sprawdzana jego poprawność. W przypadku ataku na bazę danych z hasłami, haker będzie mieć tylko tabelę z hasłami zahashowanymi oraz będzie mieć problem z odwróceniem procesu hashowania. Biblioteki BCrypt znajdziecie w Google.
Uwaga! Np. SHA-1, SHA-256 przestał być bezpiecznym algorytmem i dziś z dobrym komputerem nie ma większych problemów z inwersją. Jeżeli zależy wam na zabezpieczeniu przeciw dobrym hakerom, to polecam poszukać w Google nowych, skuteczniejszych zabezpieczeń. Jednak jeśli chcecie się obronić przed „Script kiddie”, to wystarczy nawet MD5. (nie warto też wrzucać tysięcy kluczy do tabeli, lecz dodawać nowe przy zakupie przez daną osobę)
Do tworzenia kluczy polecam stworzyć program. Ja uznałem że moje klucze będą 16 znakowe. Tak prezentuje się kod w Javie do wygenerowania losowego klucza i zhashowania go. Potrzebna jest też biblioteka BCrypt dla Javy: link .
BCrypt.gensalt(10)
– decyduje o złożoności hasła. Czym większa liczba w argumencie, tym trudniej będzie odszyfrować. Ale też sprawdzanie za pomocą serwera będzie dłuższe.
BCrypt.gensalt()
generuje optymalną wartość, wybraną przez twórcę biblioteki. Warto zauważyć, że duża wartość może być też problemem dla serwera przy sprawdzaniu poprawności klucza.
String AB
– w tym łańcuchu znaków znajdują się wszystkie znaki, które będą mogły znaleźć się w kluczu niezaszyfrowanym.
funkcja
randomString
– zwraca losowy łańcuch znaków o długości
len
, z wykorzystaniem znaków z łańcucha znaków
String AB
.
Dodajmy od razu testowy klucz
1234567890123456.
Aby taki zrobić wystarczy w kodzie zamiast
String key = randomString(16);
dać
String key = "1234567890123456";
Klucze w takiej formie będą przechowywane w tabeli do której nikt nie ma prawa mieć dostępu. Możecie zrobić bazę danych albo po prostu wklejać je do arkusza kalkulacyjnego. Otrzymujemy zaszyfrowany klucz –
$2a$10$SjT/4zh1LwNrsiKCvhUlzec4bAilAsvhw3xb2vpJkRK6eTacCTJFy.
Teraz wpisujemy go do tabeli na serwerze korzystając z komendy SQL
INSERT INTO
.
INSERT INTO `activation_keys`(`activation_key`,`used_num`) VALUES ("
$2a$10$SjT/4zh1LwNrsiKCvhUlzec4bAilAsvhw3xb2vpJkRK6eTacCTJFy
",0);
Czyli zhashowane klucze dodajemy do tabeli SQL, a klucz niezhashowany wysyłamy klientowi. Aplikacja będzie hashować klucz wpisany przez klienta, następnie wysyła go do serwera, który będzie porównywać wszystkie zhashowane klucze.
Okej, więc mamy bazę danych z kluczami gotową. Przejdźmy teraz do pliku weryfikującego licencję. Aby program zadziałał, w swojej lokalizacji musi mieć plik
activation.key
, który po odszyfrowaniu kluczem publicznym RSA wskaże
SHA-256 identyfikatora sprzętowego
urządzenia, na którym jest odpalany właśnie ten program. Ten plik
activation.key
będzie tworzony podczas pierwszej aktywacji (serwer wyśle odpowiedni kod dla klienta, który następnie zostanie zapisany w owym pliku). Trudno stworzyć taki klucz, żeby po odszyfrowaniu kluczem publicznych, dał SHA-256 identyfikatora, dlatego to zabezpieczenie jest w miarę bezpieczne. Serwer, posiadając klucz prywatny RSA może z łatwością stworzyć klucz weryfikacyjny. Rysunek pomoże dobrze zinterpretować to co napisałem:
Dla przypomnienia – schemat szyfrowania RSA . Wszystko pokazane na obrazku wyżej: kluczem publicznym szyfrujemy wiadomość; zaszyfrowanej wiadomości nie można rozszyfrować kluczem publicznym , potrzebny jest do tego klucz prywatny . Szyfrowanie to może też działać w drugą stronę – można szyfrować kluczem prywatnym, a odszyfrowywać publicznym, co dziś wykorzystamy. W naszym przypadku klucz weryfikacyjny będzie szyfrowany kluczem prywatnym na serwerze , a potem klucz będzie odszyfrowywany w aplikacji kluczem publicznym.
Jak stworzyć klucz
RSA
? Do jego stworzenia wykorzystam narzędzie
OpenSSL
, które można pobrać tu dla Windowsa:
http://gnuwins32.sourceforge.net/packages/openssl.htm
Program ten odpala się w trybie konsolowym. Potrzebne nam będzie parę komend:
openssl genrsa -out private_key.pem 2048
– tworzy 2048-bitowy klucz prywatny, o rozszerzeniu *.pem, które dobrze odczyta PHP. Jeśli chcecie lepszy poziom zabezpieczeń, to możecie użyć 3072 bitów.
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
– tworzy plik private_key.der w formacie PKCS#8, który lepiej jest odczytywany w Javie (bez kombinowania)
openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
– tworzy plik public_key.der, który łatwiej odczytać w Javie (bez tworzenia przeróżnych funkcji które zmieniają format klucza)
Są klucze, więc przyszła część na PHP, do którego będzie się zwracał klient. PHP będzie wypisywać proste komunikaty typu „ERROR”, „OK”, „WRONGKEY”, które następnie program wczyta i wskaże odpowiedni komunikat.
Klucz i identyfikator urządzenia będą wysyłane metodą POST. Wynik będzie wypisany funkcją echo, a oto lista możliwych wyników:
Po co te średniki w odpowiedziach? Są one po to aby wydzielić granice klucza weryfikacyjnego. Głębsze wyjaśnienie tego znajdziecie w dalszej części artykułu.
Program desktopowy będzie sprawdzać, jaki kod został otrzymany i wyświetli odpowiedni komunikat.
Na początku stworzyłem sobie dwie funkcje, które kończą wykonywanie pliku PHP i zwracają błąd:
Jeśli jakimś cudem nie ma parametrów HTTP POST lub identyfikator sprzętowy jest pusty to należy zakończyć działanie programu:
Klucz prywatny (private_key.pem) wrzucamy do poprzedniego katalogu niż jest plik PHP. Upewnijmy się, że prawa do pliku są tylko dla serwera i że nie można tego pliku otworzyć bezpośrednio z innego urządzenia.
Teraz należy pobrać klucz prywatny RSA z poprzedniego katalogu, który nie jest dostępny dla nikogo:
Połączenie się z bazą danych i ewentualne zwrócenie błędu:
Pobieramy rekordy z tabeli activation_keys i sprawdzamy, czy klucz jest dobry.
password_verify()
– sprawdza czy klucz w formie niezaszyfrowanej, jest odpowiednikiem zaszyfrowanego podanego w drugim argumencie (BCrypt)
trim()
– usuwa spacje na początku i końcu klucza. (zabezpieczenie, jeśli do bazy danych daliśmy zaszyfrowany klucz z niepotrzebną spacją – mi się to zdarzyło)
Jeżeli klucz nie został jeszcze użyty – czyli
$used
jest równe 0, to przygotujemy klucz weryfikacyjny dla klienta metodą
openssl_private_encrypt($informacja_do_zaszyfrowania, $zmienna_gdzie_zapisze_sie_zaszyfrowany_tekst, $klucz_prywatny_RSA);
Następnie należy powiększyć licznik użycia klucza i dodać identyfikator sprzętowy do tabeli
activations
. Na sam koniec wystarczy zwrócić klientowi klucz weryfikacyjny funkcją
echo
, który następnie klient zapisze do pliku.
Użyte jest to proste zabezpieczenie przed SQL Injection, więcej o tym możecie poczytać w innym artykule na tej stronie: Zabezpieczenia baz danych
Jeżeli klucz jest użyty to należy sprawdzić, czy urządzenie o identyfikatorze wysłanym w HTTP POST występuje w tabeli
activations
. Jeśli występuje to należy klucz weryfikujący wysłać klientowi.
Jeśli po zakończeniu pętli nie odnaleziono klucza, to należy zwrócić błąd – zły klucz.
Cały plik prezentuje się tak:
Możecie stworzyć też prosty plik HTML i porobić parę testów:
W tym fragmencie artykułu będę opisywał stworzenie aplikacji desktopowej Javie, co nie zmienia faktu, że tak samo da się zrobić w innych językach programowania.
Zacznijmy od stworzenia projektu i dodania biblioteki BCrypt. Link do biblioteki: http://www.mindrot.org/projects/jBCrypt/
Stwórzmy najpierw klasę pomocniczą
Activation.java
. Będzie ona pomagać w tworzeniu klucza identyfikatora urządzenia i do odszyfrowania klucza weryfikacyjnego:
Jak pewnie zauważyliście, do pobrania identyfikatora sprzętowego potrzebna jest magiczna klasa WindowsID. Wyżej napisałem, jak w przypadku systemu Windows pobierać identyfikator. Oto ta klasa:
W przypadku innych systemów też znajdziecie różne rozwiązania na pobranie identyfikatora sprzętowego.
Przejdźmy teraz do GUI aplikacji. Na początku można zrobić prosty layout w JavaFX (Java Scene Builder):
A tutaj przegląd obiektów, dla których przypisałem jakieś id. Jest to potrzebne po to, aby już w kodzie Javy móc pobrać te obiekty:
Do katalogu
resources
(jeśli nie wiecie jak dodawać to zapraszam tutaj:
link
) wrzucamy plik
public_key.der
. Startowy plik Javy wygląda tak:
OnActivationListener
zawiera funkcję, która zostanie aktywowana, jeśli aktywacja się powiedzie oraz funkcję pobrania klucza publicznego.
Teraz część kontrolera Aktywacji. W JavaFX, korzystając z kontrolera scen, można łatwo pobierać obiekty GUI dla których przypisaliśmy jakiś identyfikator. Wystarczy podać adnotację
@FXML
a następnie klasę obiektu i jego identyfikator. Bardzo ułatwia to pracę.
Po kliknięciu przycisku submitButton jest on wyłączany (na czas próby połączenia z serwerem) i są wykonywane czynności łączenia. Do funkcji
connectToServer
dodałem 3 argumenty:
keyInput.getText()
– klucz produktu
id
– identyfikator urządzenia
ActivationListener
– informuje o rezultacie połączenia / zwraca wynik wykonania PHP
Interfejs
ActivationListener
wygląda tak:
W odpowiedzi serwera musimy sprawdzić wszystkie przypadki, które wymieniłem wcześniej, czyli:
OK
,
OK_USED
,
WRONGKEY
,
WRONGHARDWARE
,
USED
,
ERROR
i jeszcze przypadek, jak coś innego zostało zwrócone (nieznany błąd). Gdy wystąpi błąd połączenia z serwerm (brak internetu lub serwer jest wyłączony) to zostanie wywołana funkcja z interfejsu
ActivationListener void error();
Błąd połączania wyskoczy jako wyjątek
IOException
, który łatwo wyłapać.
A tak wygląda sama funkcja łączenia z serwerem:
Warto zaznaczyć, że koniecznie trzeba tu użyć wątków. Bez nich aplikacja zacięłaby się na czas łączenia z serwerem.
Tak nie może być!
Dlatego też skorzystałem z prostego wątku
Thread.
Wracamy do interfejsu
ActivationListener
. Co jeśli jest błąd połączenia? Należy wyświetlić odpowiedni komunikat i ponownie włączyć przycisk
submitButton
(bo użytkownik ma prawo ponowić próbę).
Warto zauważyć, iż aktualizując elementy interfejsu z innego wątku, należy skorzystać z funkcji
Platform.runLater
, która aktualizuje GUI z wątku interfejsu graficznego. Bez tego nie da się aktualizować interfejsu.
Przejdźmy teraz do drugiej funkcji interfejsu
ActivationListener.
Serwer zwraca pewien ciąg bajtów, które można zamienić na łańcuch znaków:
String
responseString
=
new
String
(response);
Problem polega na tym, że PHP zwraca niepotrzebne spacje i nowe linie na końcu pliku, dlatego też użyłem średników do wyznaczania początku i końca klucza weryfikacyjnego. Do tego klucz weryfikacyjny jest zapisany w
formie bitów
i zawiera różne
znaki, które po przekształceniu do String’a,
zmienią
swój format. Dlatego, w przypadku zwróconego klucza weryfikacyjnego przez serwer, trzeba na nim operować jako na
tablicy bajtów
. Funkcja
trimKey
obcina tablicę bajtów do pierwszej znalezionej liczby podanej w argumencie 2 oraz obcina tablicę od końca to ostatniej znalezionej liczby podanej w argumencie 2. Średnik to w kodzie ASCII numer 59, dlatego też taki wpisałem do funkcji
trimKey
. Dobry klucz jest pobierany do zmiennej
goodKey
, a do zmiennej
keyFromServerDecrypted
przypisywany jest odszyfrowany klucz weryfikacyjny. Jeśli oba te zmienne mają takie same wartości (
contentEquals
), to należy zapisać klucz zaszyfrowany w pliku
activation.key
. Starałem się okomentować kod, aby był w miarę możliwości zrozumiały:
A funkcja
trimKey
prezentuje się tak:
Na sam koniec wystarczy jeszcze dopieszczyć plik startowy, aby program nie wyświetlał okna z aktywacją przy każdym uruchomieniu. Klucz będzie sprawdzany tak samo jak w przypadku aktywacji, więc nie tłumaczę już kodu. Poniżej fragment kodu z funkcji startowej JavaFX:
Nareszcie koniec ! Cieszę się, jeśli dotrwaliście do tego momentu. Od teraz możecie powiedzieć, że algorytm RSA nie taki straszny, a nasza aplikacja stała się w miarę bezpieczna! Jakbyście czegoś nie rozumieli to zapraszam do komentowania !
Tworząc aplikacje, które dotrą do wielkiej liczby użytkowników, nie jesteśmy w stanie całkiem przeciwdziałać ich niecnym zamiarom okradania nas. Jednakże systemy antypirackie takie jak aktywacja online pozwalają pozbyć się tego problemu nawet w większości przypadków. Warto łączyć różne systemy aktywacji, tworzyć własne, aby były trudniejsze do ogarnięcia .
W tym artykule przekazałem także informacje na temat szyfrowania – jest ono używane wszędzie: na stronach banku , sklepach, mediach społecznościowych. Dlatego kryptografia (utajnianie wiadomości) jest taka ważna. Korzystamy z niej na co dzień! Tworząc stronę internetową warto zadbać o to, aby hasła użytkowników nie były narażone na proste ataki hakerów. Zabezpieczenie haseł nie jest trudnym zajęciem, jednakże nadal sporo osób omija to szerokim łukiem lub używa przestarzałych algorytmów.
]]>
std::array
.
Zakładam, że wiesz co to tablica, prawda? Ot, zbiór elementów jednego typu poukładanych w pamięci jeden za drugim. Nic strasznego, nic trudnego. Jest to bardzo prosta rzecz, można by rzec nawet, że aż za prosta – standardowy C-style array nie posiada w sobie nic ciekawego, żadnych iteratorów, żadnych fajnych metod dostępu do niego, wszystko trzeba robić „ręcznie”.
I tutaj na scenie pojawia się
std::array
z biblioteki
array
, dodany w standardzie
C++11
Ważne – (zazwyczaj) domyślnie każdy kompilator korzysta ze standardu C++03. Żeby skorzystać z nowszych, należy to sprecyzować przy użyciu flagi -std=c++11/14/17 (zależnie od standardu), albo w opcjach kompilatora – ale to zostawiam już do samodzielnego ogarnięcia).
Jest to bardzo przyjemna alternatywa, posiadająca dużą wartość użytkową – po prostu ułatwia programiście życie. No dobra, ale co w tym takiego fajnego? Dlaczego miałbym się przestawić nagle na
std::array
, zamiast używać starego dobrego c-style arraya?
Używanie
std::array
jest prawie takie samo jak używanie zwykłego c-style arraya – losowy dostęp do elementów umożliwia standardowy operator
[]
, ale oprócz tego ten kontener posiada kilka bajerów:
Po pierwsze –
std::array
posiada tak zwany
iterator
. Iterator to obiekt wskazujący na jakiś element z zakresu elementów, który potrafi iterować (czyli, potocznie mówiąc,
„przeskakiwać”
) między elementami przy pomocy na przykład operatorów inkrementacji czy dekrementacji (ale częściej iteratorów używa się ich w range-based for’ach na przykład, o czym za chwilę). Jest to duże ułatwienie w sytuacjach gdzie musimy na przykład przelecieć całą tablicę od początku do końca (lub też od końca do początku, bo
std::array
posiada także reverse iterator!)
Po drugie – posiada przeładowane operatory porównania
==
,
!=
,
<
,
<=
,
>
,
>=
. Czyli możemy w łatwy sposób porównywać dwa std::array’e. Dodatkowo, ma przeładowania dla
std::swap
i
std::get
, gdybyśmy potrzebowali w fikuśny sposób dostać się do danego elementu tablicy lub zamienić jej zawartość z zawartością innej.
Po trzecie – posiada kilka użytkowych metod, takich jak
fill
(wypełnienie tablicy pewną wartością),
front
/
back
(dostęp do pierwszego/ostatniego elementu),
empty
(sprawdzenie czy tablica jest pusta),
size
/
max_size
(sprawdzenie aktualnej i maksymalnej ilości elementów). Jest to też typ agregowalny, czyli przykładowo można go inicjalizować poprzez klamry
{}
, tak jak zwykłą tablicę.
Podsumowując –
std::array
jest prawie taki sam w użytkowaniu jak zwykły c-style array, tylko fajniejszy oraz często wygodniejszy.
Jak już wcześniej mówiłem, można używać
std::array
tak samo jak zwykłej tablicy. Inaczej wygląda deklaracja, ponieważ jest to zwyczajna struktura z szablonowymi typami w konstruktorze:
template<class T, std::size_t N> struct array;
Gdzie T to typ danych przechowywanych w naszym kontenerze, a N to jego początkowa wielkość.
std::size_t
to ogólnie mówiąc typ danych dla liczb całkowitych bez znaku.
Warto o tym pamiętać, polecam przy odnoszeniu się do wielkości kontenerów używać
std::size_t
zamiast int’a czy jakiegokolwiek innego typu danych, oszczędzi nam to ostrzeżeń kompilatora mówiących o porównywaniu typów ze znakiem i bez niego.
Przykładowe deklaracje takich kontenerów wyglądają następująco
No i fajnie, mamy jakieśtam tablice. Co teraz z nimi zrobić? Wykorzystajmy dla przykładu iteratory.
Ten kod chyba wymaga tłumaczenia, szczególnie jeśli ktoś wcześniej nie miał kontaktu z nowszymi standardami C++.
Więc tak – pierwszy przykład wyświetla nam zawartość
std::array
przy użyciu range-based for’a.
const auto &v
to stała referencja do kolejnych wartości kontenera, iterator automatycznie inkrementuje się co iterację pętli. Dlaczego odnoszę się do elementów w ten sposób? Mógłbym to zrobić klasycznie, poprzez
int v
, ale w ten sposób każdy z elementów byłby kopiowany do tymczasowej zmiennej
v
, co w przypadku większych kontenerów stwarzało by duży problem wydajnościowy. Odniesienie się poprzez referencję pozwala na kopiowanie jedynie adresu do elementu, unikając kopiowania jego samego, a
const
uniemożliwia zmianę danego elementu (bo nie chcemy go przecież zmieniać). W tym wypadku jest to na tyle mała skala że w praktyce nie zauważymy żadnych wzrostów wydajności, ale warto wyrobić sobie praktykę używania referencji. Jeśli chodzi o
auto
, to jest to tylko kwestia wygody – ten keyword i tak zostanie zamieniony przez kompilator na odpowiedni typ danych.
W następnym kawałku kodu wypełniamy sobie tablicę bezpośrednio odnosząc się do iteratorów –
b.begin()
wskazuje na początek,
b.end()
na koniec naszego kontenera (analogicznie,
b.rbegin()
wskazuje na koniec, a
b.rend()
na początek). Jak wcześniej mówiłem, iteratory mają przeładowane operatory – inkrementacja, dekrementacja i tego typu rzeczy, które są bardzo wygodne.
Dalej, modyfikacja elementów przy użyciu range-based for’a, tutaj używam referencji bez
const
ponieważ modyfikuję elementy (i w tym wypadku nie mogę odnieść się bez referencji, ponieważ bez niej operował bym na kopiach elementów tablicy)
I na koniec smaczek, przelatuję sobie tablicę od końca co drugi element.
Oczywiście to tylko kilka przykładowych porównań, ale widzimy, że
std::array
jest o wiele bardziej czytelniejszy niż standardowa tablica.
Przyszedł czas na krótkie podsumowanie naszego kontenera
std::vector
I w sumie tyle. Jeśli masz jakieś wątpliwości, nadal czegoś nie rozumiesz, zapomniałem o czymś, masz jakąś sugestię czy cokolwiek – pisz śmiało w komentarzu.
A po pełną referencję i więcej przykładów dla tego kontenera zapraszam tutaj: http://en.cppreference.com/w/cpp/container/array .
Następny rozdział będzie poświęcony kontenerowi
std::vector.
Jako nastoletni programiści nieraz mamy taki moment, że nie wiemy co mamy robić. Kolejna strona/program?
Brak pomysłów
! Więc czemu by nie sprawdzić siebie w zleceniach? W tym artykule chciałbym omówić tę kwestię.
Zlecenia to dobry sposób na nabycie doświadczenia.
W Polsce najpopularniejszym serwisem od zleceń dla programistów z pewnością jest Oferia.pl . Na tej stronie znajdziecie pełno zleceń – od budownictwa, grafik, transportu, aż po właśnie programowanie. Aktualnie jest 1300+ zleceń, które możemy wykonywać. Myślę, że każdy może tu znaleźć coś dla siebie – najwięcej zleceń jest z PHP, C++, C#, HTML i Javy.
– jest dwuetapowa. Najpierw należy zrobić konto, podając adres email i hasło, a następnie po aktywacji przystąpić do konfiguracji konta. Co ważne – nie musicie mieć 18 lat ! W całym regulaminie Oferii nie ma wzmianki o wieku. Co więcej, nie musicie go nawet podawać! Z rejestracją jest tylko jeden mały problem – nasze dane osobowe będą widoczne w Google . Niektórym będzie to przeszkadzać, ale cóż, z tym nie da się nic zrobić. Podczas rejestracji należy podać imię, nazwisko, telefon i adres zamieszkania.
Jak to z legalnością? – tutaj w większości zależy od zleceniodawcy. Zdecydowana większość zleceniodawców woli nie mieć żadnych umów . Większość tutaj kontaktuje się przez waszego maila, całkowicie omijając potem serwis Oferia – co najwyżej potem mogą wystawić wam ocenę, jeśli poprosicie. Zrobiłem już parę zleceń i ani razu nie miałem dalszego kontaktu przez ten serwis, nawet ocen nie dostałem.
„Wielu spotkałem takich ludzi, którzy chcieliby oszukiwać, ale takiego, który by chciał być oszukiwany, nie spotkałem.” – Aureliusz Augustyn z Hippony
Skoro bez umowy, to łatwo nas okraść? – tak, to niestety jest prawda . Oczywiście, możesz wymagać takowej umowy, ale szczerze mówiąc nie trafiłem jeszcze na kogoś, kto by mnie oszukał. A nawet jakby ktoś oszukał, to chciałoby wam się „gonić” za tą osobą? Oczywiście to zależy od ceny zlecenia, lecz na sam początek proponuję nie brać tych droższych – lepiej oswoić się z zleceniami, robiąc coś prostego.
Trafiają się też osoby, które najpierw wymagały mniej, a potem okazało się, że chcą jeszcze „to i tamto”. Wtedy musicie zadecydować – albo robicie w tej cenie, albo cena wzrośnie.
Wracając trochę do wstępu – jak nie macie co robić i robicie w miarę tanie zlecenia, to nawet jeśli dalibyście się zrobić w konia (dość rzadko się to zdarza), to zawsze macie kolejny projekt do portfolio ! Nie każdy ma motywację, żeby robić sobie samemu takie projekty do portfolio, a dzięki takim zleceniom macie ciekawe pomysły + uczycie się nowych technologii! Jest wiele prostych aplikacji, których nie chciałoby wam się robić, bo wydają się wam niepotrzebne, a jest wiele osób, które potrzebują takie aplikacje. Np. studenci często potrzebują prostych zadań – wyświetlanie figur 3D, prosty odtwarzacz muzyki, liczenie kredytów – to dobra okazja, aby nauczyć się jakiegoś API!
Pewnie teraz zapytacie – czyli jak składam ofertę, to muszę umieć perfekcyjnie to, co jest w zleceniu ? – odpowiedź brzmi: nie ! Jeżeli w ofercie jest wymagane używanie jakiegoś frameworka, to przejrzyj go z grubsza, oceń czy dasz radę i po prostu napisz ofertę. Zleceń nie trzeba wykonywać w 1 dzień – nieraz firmy robią dłużej projekty niż osoby prywatne. Skoro się jednocześnie uczysz i robisz zlecenia, to możesz dodać konkurencyjną cenę!
– to jest najważniejsze. Po przeczytaniu waszej oferty zleceniodawcy często patrzą na portfolio. Na Oferii
macie możliwość tworzenia portfolio, jednakże ja polecam
własną stronę
– bo to po prostu ładniej wygląda. Kolejne projekty będziecie tam wrzucać i będzie to wszystko ładnie rosło. Ja jeszcze nie mam, ale musicie uwierzyć mi na słowo.
Bez portfolio trudno dostać dobrą ofertę. Wyobraźcie siebie na miejscu zleceniodawcy – chcecie mieć dobry program/stronkę, a nie wykonaną przez gimnazjalistę w Scratchu. W opisie oferty zawsze odpowiadaj dokładnie na to, co prosi zleceniodawca – tzn. jeśli chce konkretną kwotę, to musisz ją podać! Uwzględnij swoje kompetencje, np. jeśli robiłeś coś podobnego w przeszłości, to wspomnij o tym. Możesz powiedzieć coś o sobie, przytoczyć inne projekty (portfolio). Dodaj, że bardzo chętnie podejmiesz się tego zlecenia i na sam koniec koniecznie podaj adres e-mail – bo, jak mówiłem, większość zleceniodawców woli kontakt przez e-mail niż przez prywatne wiadomości serwisu Oferia. Ogólnie jest dużo osób, które nawet nie czytają zlecenia, kopiują swoją ofertę i wklejają do wielu ofert (z ceną „do negocjacji”) – Ty już jesteś od nich lepszy !
Jak pewnie zauważysz, niektóre oferty są podświetlone – jest to pakiet MAX na Oferii. To już do was należy przemyśleć, czy chcecie taki kupić – z doświadczenia powiem, że bez tego z pewnością znajdziecie zlecenia. Opis i tak jest ważniejszy!
Gdy już znajdziesz odpowiednie zlecenie, to często inni wykonawcy będą podbijać i edytować swoje oferty. Możesz tutaj troszkę oszukać system i napisać coś więcej – ale wtedy licz się z tym, że zleceniodawca będzie wymagał od Ciebie profesjonalizmu. Pamiętaj też, że najniższa cena niczego nie gwarantuje. Jeśli nie masz portfolio, a ofertę masz kiepską, to najniższa cena nic nie pomoże.
Wysyłajcie wiele ofert – z doświadczenia wiem, że niektórzy zleceniodawcy rezygnują z projektu i do nikogo nie piszą. Jeśli jest jakiekolwiek nowe zlecenie, które Ci się podoba – pisz od razu . Jeśli nawet trafi Ci się potem tak, że napisze do Ciebie paru gości na maila, to po prostu powiesz im, że aktualnie nie dasz rady tego wykonać.
Podczas wykonywania zleceń możecie trafić na osobę, która w przyszłości będzie wam dostarczać zlecenia. Wasze dane osobowe są teraz publiczne, więc może się zdarzyć, że zleceniodawcy do was zadzwonią: ja na przykład otrzymałem ofertę stworzenia systemu dla biblioteki (spis książek, aplikacja webowa i Androidowa). Co więcej – nawet dzwoniła do mnie firma, która szuka programistę Java. Niestety jako iż jeszcze się uczę, a praca miała być na cały etat, to musiałem odmówić.
Inne strony ( żeby nie było, że to reklama Oferii ) – Z innych stron korzystałem też z freelancer.com . Tam jest baaaardzo dużo ofert (w końcu nie tylko polskie), jednak jakoś nie miałem tam powodzenia. Jest tam też dużo większy wybór pakietów (i dużo droższych) + większy limit ofert na miesiąc. Ale spróbować na pewno warto .
Przywołując słowa Alberta Einsteina:
„Każda praca jest dobra, o ile jest dobrze wykonywana.”
starajmy się, aby każde nasze zlecenie było dobrze wykonywane. Czym więcej zadowolonych klientów, to tym więcej doświadczenia i szansa na zauważenie takiej osoby przez znane firmy. Wykonując dobrą robotę, dostaniecie dobrą pracę, więc jeśli jeszcze nie wykonywałeś żadnej aplikacji na zlecenie – spróbuj !
]]>